diff --git a/dev_odex30_accounting/account_chart_of_accounts/__manifest__.py b/dev_odex30_accounting/account_chart_of_accounts/__manifest__.py index 2d53a00..c2f07aa 100644 --- a/dev_odex30_accounting/account_chart_of_accounts/__manifest__.py +++ b/dev_odex30_accounting/account_chart_of_accounts/__manifest__.py @@ -41,11 +41,14 @@ 'account_chart_of_accounts/static/src/js/account_type_selection_extend.js', 'account_chart_of_accounts/static/src/js/filters_patch.js', 'account_chart_of_accounts/static/src/js/account_report.js', + 'account_chart_of_accounts/static/src/js/account_list_renderer.js', + 'account_chart_of_accounts/static/src/js/search_panel_bold.js', 'account_chart_of_accounts/static/src/xml/filter_full_hierarchy.xml', - ], - 'web.assets_frontend': [ + # CRITICAL: SCSS must be in backend, not frontend! 'account_chart_of_accounts/static/src/scss/account_hierarchy.scss', + ], + }, 'installable': True, diff --git a/dev_odex30_accounting/account_chart_of_accounts/i18n/ar_001.po b/dev_odex30_accounting/account_chart_of_accounts/i18n/ar_001.po new file mode 100644 index 0000000..84c3335 --- /dev/null +++ b/dev_odex30_accounting/account_chart_of_accounts/i18n/ar_001.po @@ -0,0 +1,317 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * account_chart_of_accounts +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 18.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2026-01-18 03:36+0000\n" +"PO-Revision-Date: 2026-01-18 03:36+0000\n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: account_chart_of_accounts +#: model_terms:ir.ui.view,arch_db:account_chart_of_accounts.view_account_form_inherit_hierarchy +msgid "" +"\n" +" Auto\n" +" " +msgstr "" + +#. module: account_chart_of_accounts +#: model:ir.model,name:account_chart_of_accounts.model_account_account +msgid "Account" +msgstr "الحساب " + +#. module: account_chart_of_accounts +#: model_terms:ir.ui.view,arch_db:account_chart_of_accounts.res_config_settings_view_form_inherit +msgid "Account Code Padding" +msgstr "حشو كود الحساب" + +#. module: account_chart_of_accounts +#: model:ir.model.fields,help:account_chart_of_accounts.field_account_account__account_type +msgid "" +"Account Type is used for information purpose, to generate country-specific " +"legal reports, and set the rules to close a fiscal year and generate opening" +" entries." +msgstr "" +"يُستخدم نوع الحساب في إنشاء التقارير القانونية المخصصة لكل دولة، وتضع " +"القواعد اللازمة لإقفال السنة المالية وإنشاء القيود الافتتاحية للسنة المالية " +"اللاحقة. " + +#. module: account_chart_of_accounts +#: model:ir.model,name:account_chart_of_accounts.model_account_report +msgid "Accounting Report" +msgstr "تقرير المحاسبة " + +#. module: account_chart_of_accounts +#. odoo-javascript +#: code:addons/account_chart_of_accounts/static/src/js/account_type_selection_extend.js:0 +msgid "Assets" +msgstr "الأصول" + +#. module: account_chart_of_accounts +#: model:ir.model.fields,field_description:account_chart_of_accounts.field_account_account__auto_code +msgid "Auto Code" +msgstr "كود آلي" + +#. module: account_chart_of_accounts +#: model_terms:ir.ui.view,arch_db:account_chart_of_accounts.res_config_settings_view_form_inherit +msgid "Auto Generate Account Codes" +msgstr "توليد أكواد الحسابات تلقائياً" + +#. module: account_chart_of_accounts +#: model:ir.model.fields,field_description:account_chart_of_accounts.field_account_account__automticAccountsCodes +#: model:ir.model.fields,field_description:account_chart_of_accounts.field_res_company__automticAccountsCodes +#: model:ir.model.fields,field_description:account_chart_of_accounts.field_res_config_settings__automticAccountsCodes +msgid "Automatically Generate Accounts Codes" +msgstr "توليد أكواد الحسابات تلقائياً" + +#. module: account_chart_of_accounts +#: model_terms:ir.ui.view,arch_db:account_chart_of_accounts.res_config_settings_view_form_inherit +msgid "Automatically generate account codes based on parent account" +msgstr "توليد أكواد الحسابات تلقائياً بناءً على الحساب الأب" + +#. module: account_chart_of_accounts +#. odoo-javascript +#: code:addons/account_chart_of_accounts/static/src/js/account_type_selection_extend.js:0 +msgid "Balance Sheet" +msgstr "الميزانية العمومية" + +#. module: account_chart_of_accounts +#: model_terms:ir.ui.view,arch_db:account_chart_of_accounts.res_config_settings_view_form_inherit +msgid "Bank Account Prefix" +msgstr "بادئة حساب البنك" + +#. module: account_chart_of_accounts +#: model:ir.model.fields,field_description:account_chart_of_accounts.field_res_config_settings__bank_account_code_prefix +msgid "Bank Prefix" +msgstr "بادئة البنك" + +#. module: account_chart_of_accounts +#. odoo-python +#: code:addons/account_chart_of_accounts/models/account_journal.py:0 +msgid "Can not find account with code (%s)." +msgstr "لا يمكن إيجاد حساب بالكود (%s)." + +#. module: account_chart_of_accounts +#. odoo-python +#: code:addons/account_chart_of_accounts/models/account_journal.py:0 +msgid "" +"Cannot generate an unused journal code. Please fill the 'Shortcode' field." +msgstr "لا يمكن توليد كود دفتر يومية غير مستخدم. يرجى ملء حقل 'الرمز المختصر'." + +#. module: account_chart_of_accounts +#: model_terms:ir.ui.view,arch_db:account_chart_of_accounts.res_config_settings_view_form_inherit +msgid "Cash Account Prefix" +msgstr "بادئة حساب النقدية" + +#. module: account_chart_of_accounts +#: model:ir.model.fields,field_description:account_chart_of_accounts.field_res_config_settings__cash_account_code_prefix +msgid "Cash Prefix" +msgstr "بادئة النقدية" + +#. module: account_chart_of_accounts +#. odoo-python +#: code:addons/account_chart_of_accounts/models/res_config_settings.py:0 +msgid "Chart of Accounts" +msgstr "دليل الحسابات" + +#. module: account_chart_of_accounts +#: model_terms:ir.ui.view,arch_db:account_chart_of_accounts.res_config_settings_view_form_inherit +msgid "Chart of Accounts Hierarchy" +msgstr "التسلسل الهرمي لدليل الحسابات" + +#. module: account_chart_of_accounts +#: model:ir.model.fields,field_description:account_chart_of_accounts.field_res_company__chart_account_padding +#: model:ir.model.fields,field_description:account_chart_of_accounts.field_res_config_settings__chart_account_padding +msgid "Chart of accounts Padding" +msgstr "حشو دليل الحسابات" + +#. module: account_chart_of_accounts +#: model:ir.model.fields,field_description:account_chart_of_accounts.field_res_company__chart_account_length +#: model:ir.model.fields,field_description:account_chart_of_accounts.field_res_config_settings__chart_account_length +msgid "Chart of accounts length" +msgstr "طول دليل الحسابات" + +#. module: account_chart_of_accounts +#: model:ir.model.fields,field_description:account_chart_of_accounts.field_account_account__child_ids +msgid "Child Accounts" +msgstr "الحسابات الفرعية" + +#. module: account_chart_of_accounts +#: model_terms:ir.ui.view,arch_db:account_chart_of_accounts.view_account_form_inherit_hierarchy +msgid "Code" +msgstr "الكود" + +#. module: account_chart_of_accounts +#: model:ir.model,name:account_chart_of_accounts.model_res_company +msgid "Companies" +msgstr "الشركات" + +#. module: account_chart_of_accounts +#: model:ir.model.fields,field_description:account_chart_of_accounts.field_account_account__company_id +msgid "Company" +msgstr "الشركة" + +#. module: account_chart_of_accounts +#: model:ir.model,name:account_chart_of_accounts.model_res_config_settings +msgid "Config Settings" +msgstr "تهيئة الإعدادات " + +#. module: account_chart_of_accounts +#: model:ir.model.fields,help:account_chart_of_accounts.field_account_report__filter_show_full_hierarchy +msgid "Enable filter to display accounts in full hierarchical structure" +msgstr "تفعيل الفلتر لعرض الحسابات في هيكل هرمي كامل" + +#. module: account_chart_of_accounts +#. odoo-javascript +#: code:addons/account_chart_of_accounts/static/src/js/account_type_selection_extend.js:0 +msgid "Equity" +msgstr "حقوق الملكية" + +#. module: account_chart_of_accounts +#. odoo-javascript +#: code:addons/account_chart_of_accounts/static/src/js/account_type_selection_extend.js:0 +msgid "Expense" +msgstr "المصروفات" + +#. module: account_chart_of_accounts +#. odoo-javascript +#: code:addons/account_chart_of_accounts/static/src/xml/filter_full_hierarchy.xml:0 +msgid "Full Hierarchy" +msgstr "التسلسل الهرمي الكامل" + +#. module: account_chart_of_accounts +#: model_terms:ir.ui.view,arch_db:account_chart_of_accounts.view_account_search_inherit +msgid "Hierarchical Chart" +msgstr "الدليل الهرمي" + +#. module: account_chart_of_accounts +#. odoo-javascript +#: code:addons/account_chart_of_accounts/static/src/js/account_type_selection_extend.js:0 +msgid "Income" +msgstr "الإيرادات" + +#. module: account_chart_of_accounts +#: model:ir.model.fields,field_description:account_chart_of_accounts.field_account_account__internal_group +msgid "Internal Group" +msgstr "مجموعة داخلية" + +#. module: account_chart_of_accounts +#: model:ir.model,name:account_chart_of_accounts.model_account_journal +msgid "Journal" +msgstr "دفتر اليومية" + +#. module: account_chart_of_accounts +#: model:ir.model.fields,field_description:account_chart_of_accounts.field_account_account__level +msgid "Level" +msgstr "المستوى" + +#. module: account_chart_of_accounts +#. odoo-javascript +#: code:addons/account_chart_of_accounts/static/src/js/account_type_selection_extend.js:0 +msgid "Liabilities" +msgstr "الخصوم" + +#. module: account_chart_of_accounts +#: model_terms:ir.ui.view,arch_db:account_chart_of_accounts.res_config_settings_view_form_inherit +msgid "Number of digits for account code padding" +msgstr "عدد الأرقام لحشو كود الحساب" + +#. module: account_chart_of_accounts +#. odoo-javascript +#: code:addons/account_chart_of_accounts/static/src/js/account_type_selection_extend.js:0 +msgid "Other" +msgstr "أخرى" + +#. module: account_chart_of_accounts +#. odoo-python +#: code:addons/account_chart_of_accounts/models/account_journal.py:0 +msgid "Outstanding Payments" +msgstr "مدفوعات معلقة" + +#. module: account_chart_of_accounts +#. odoo-python +#: code:addons/account_chart_of_accounts/models/account_journal.py:0 +msgid "Outstanding Receipts" +msgstr "مقبوضات معلقة" + +#. module: account_chart_of_accounts +#: model:ir.model.fields,field_description:account_chart_of_accounts.field_account_account__parent_id +msgid "Parent Account" +msgstr "الحساب الأب" + +#. module: account_chart_of_accounts +#: model:ir.model.fields,field_description:account_chart_of_accounts.field_res_company__parent_bank_cash_account_id +msgid "Parent Bank Cash Account" +msgstr "حساب البنك النقدي الأب" + +#. module: account_chart_of_accounts +#: model:ir.model.fields,field_description:account_chart_of_accounts.field_account_account__parent_path +msgid "Parent Path" +msgstr "المسار الأب" + +#. module: account_chart_of_accounts +#: model_terms:ir.ui.view,arch_db:account_chart_of_accounts.res_config_settings_view_form_inherit +msgid "Prefix for bank account codes" +msgstr "بادئة لأكواد حسابات البنك" + +#. module: account_chart_of_accounts +#: model_terms:ir.ui.view,arch_db:account_chart_of_accounts.res_config_settings_view_form_inherit +msgid "Prefix for cash account codes" +msgstr "بادئة لأكواد حسابات النقدية" + +#. module: account_chart_of_accounts +#. odoo-javascript +#: code:addons/account_chart_of_accounts/static/src/js/account_type_selection_extend.js:0 +msgid "Profit & Loss" +msgstr "الأرباح والخسائر" + +#. module: account_chart_of_accounts +#: model:ir.model.fields,field_description:account_chart_of_accounts.field_account_report__filter_show_full_hierarchy +msgid "Show Full Hierarchy Filter" +msgstr "عرض فلتر التسلسل الهرمي الكامل" + +#. module: account_chart_of_accounts +#. odoo-python +#: code:addons/account_chart_of_accounts/models/account_account.py:0 +msgid "This account level is greater than the chart of account length." +msgstr "مستوى هذا الحساب أكبر من طول دليل الحسابات." + +#. module: account_chart_of_accounts +#: model:ir.model,name:account_chart_of_accounts.model_account_trial_balance_report_handler +msgid "Trial Balance Custom Handler" +msgstr "معالج مخصص لميزان المراجعة " + +#. module: account_chart_of_accounts +#: model:ir.model.fields,field_description:account_chart_of_accounts.field_account_account__account_type +msgid "Type" +msgstr "النوع" + +#. module: account_chart_of_accounts +#: model:ir.model.fields,field_description:account_chart_of_accounts.field_res_company__useFiexedTree +#: model:ir.model.fields,field_description:account_chart_of_accounts.field_res_config_settings__useFiexedTree +msgid "Use Fixed Length Chart of accounts" +msgstr "استخدام دليل حسابات بطول ثابت" + +#. module: account_chart_of_accounts +#: model_terms:ir.ui.view,arch_db:account_chart_of_accounts.res_config_settings_view_form_inherit +msgid "Use fixed length chart of accounts structure" +msgstr "استخدام هيكل دليل حسابات بطول ثابت" + +#. module: account_chart_of_accounts +#: model:ir.model.fields.selection,name:account_chart_of_accounts.selection__account_account__account_type__view +#: model:ir.model.fields.selection,name:account_chart_of_accounts.selection__account_account__internal_group__view +msgid "View" +msgstr "إجمالي" + +#. module: account_chart_of_accounts +#: model_terms:ir.ui.view,arch_db:account_chart_of_accounts.view_account_form_inherit_hierarchy +msgid "e.g. 101000" +msgstr "مثال: 101000" \ No newline at end of file diff --git a/dev_odex30_accounting/account_chart_of_accounts/models/__pycache__/account_account.cpython-311.pyc b/dev_odex30_accounting/account_chart_of_accounts/models/__pycache__/account_account.cpython-311.pyc index 9787f84..4be3921 100644 Binary files a/dev_odex30_accounting/account_chart_of_accounts/models/__pycache__/account_account.cpython-311.pyc and b/dev_odex30_accounting/account_chart_of_accounts/models/__pycache__/account_account.cpython-311.pyc differ diff --git a/dev_odex30_accounting/account_chart_of_accounts/models/account_account.py b/dev_odex30_accounting/account_chart_of_accounts/models/account_account.py index 0b1a7ed..0ed4c60 100644 --- a/dev_odex30_accounting/account_chart_of_accounts/models/account_account.py +++ b/dev_odex30_accounting/account_chart_of_accounts/models/account_account.py @@ -139,49 +139,56 @@ class AccountAccount(models.Model): @api.model def search_panel_select_range(self, field_name, **kwargs): - + """ + Override to show hierarchical accounts in search panel. + Root level: Only show view accounts with children + Sub-levels: Show all children when parent is expanded + """ if field_name != 'parent_id': return super().search_panel_select_range(field_name, **kwargs) - enable_counters = kwargs.get('enable_counters', False) search_domain = kwargs.get('search_domain', []) + # Get ALL accounts for hierarchy + all_accounts = self.search([], order='code') + + # Filter accounts to show: + # - Root level (parent_id = False): ONLY view accounts WITH children + # - Sub levels: ALL accounts (shown when parent is expanded) + accounts_to_show = all_accounts.filtered( + lambda a: (not a.parent_id and a.account_type == 'view' and a.child_ids) or a.parent_id + ) - all_view_accounts = self.search([('account_type', '=', 'view')], order='code') - - count_by_parent = {} + count_by_account = {} if enable_counters: - all_accounts = self.search(search_domain) - - for account in all_accounts: - if account.parent_id: - parent_id = account.parent_id.id - count_by_parent[parent_id] = count_by_parent.get(parent_id, 0) + 1 - - ancestor = account.parent_id.parent_id - while ancestor: - count_by_parent[ancestor.id] = count_by_parent.get(ancestor.id, 0) + 1 - ancestor = ancestor.parent_id - + # Count records that match the search domain for each account + filtered_accounts = self.search(search_domain) + + for account in filtered_accounts: + # ✅ ONLY count in parent (and ancestors), not self + parent = account.parent_id + while parent: + count_by_account[parent.id] = count_by_account.get(parent.id, 0) + 1 + parent = parent.parent_id values = [] - for parent in all_view_accounts: + for account in accounts_to_show: value = { - 'id': parent.id, - 'display_name': f"{parent.code} {parent.name}" if parent.code else parent.name, - 'parent_id': parent.parent_id.id if parent.parent_id else False, + 'id': account.id, + 'display_name': f"{account.code} {account.name}" if account.code else account.name, + 'parent_id': account.parent_id.id if account.parent_id else False, } if enable_counters: - value['__count'] = count_by_parent.get(parent.id, 0) + value['__count'] = count_by_account.get(account.id, 0) values.append(value) - result = { 'parent_field': 'parent_id', 'values': values, } return result + diff --git a/dev_odex30_accounting/account_chart_of_accounts/static/src/js/account_list_renderer.js b/dev_odex30_accounting/account_chart_of_accounts/static/src/js/account_list_renderer.js new file mode 100644 index 0000000..d4998fc --- /dev/null +++ b/dev_odex30_accounting/account_chart_of_accounts/static/src/js/account_list_renderer.js @@ -0,0 +1,31 @@ +/** @odoo-module **/ + +import { ListRenderer } from "@web/views/list/list_renderer"; +import { patch } from "@web/core/utils/patch"; + +patch(ListRenderer.prototype, { + /** + * Add custom class to rows with account_type='view' to make them bold + */ + setup() { + super.setup(...arguments); + }, + + async onCellClicked(record, column, ev) { + await super.onCellClicked(...arguments); + }, + + getRowClass(record) { + const classes = super.getRowClass(record) || ""; + + // Add bold class if account_type is 'view' + if (this.props.list.resModel === 'account.account') { + const accountType = record.data.account_type; + if (accountType === 'view') { + return `${classes} fw-bold account-view-type`; + } + } + + return classes; + } +}); diff --git a/dev_odex30_accounting/account_chart_of_accounts/static/src/js/search_panel_bold.js b/dev_odex30_accounting/account_chart_of_accounts/static/src/js/search_panel_bold.js new file mode 100644 index 0000000..45aa5fb --- /dev/null +++ b/dev_odex30_accounting/account_chart_of_accounts/static/src/js/search_panel_bold.js @@ -0,0 +1,61 @@ +/** @odoo-module **/ + +import { patch } from "@web/core/utils/patch"; +import { SearchPanel } from "@web/search/search_panel/search_panel"; + +patch(SearchPanel.prototype, { + /** + * Override to add bold class to view accounts with children in search panel + */ + setup() { + super.setup(...arguments); + }, + + /** + * After rendering, add bold class to accounts with toggle button + */ + async onMounted() { + if (super.onMounted) { + await super.onMounted(...arguments); + } + this.makeBoldAccountsWithChildren(); + }, + + /** + * Make accounts with children (that have toggle button) bold + */ + makeBoldAccountsWithChildren() { + // CRITICAL: HTML uses class "account_root" not "account_account" + const searchPanel = document.querySelector('.o_search_panel.account_root'); + if (!searchPanel) return; + + // Find ALL category values + const allItems = searchPanel.querySelectorAll('.o_search_panel_category_value'); + + allItems.forEach(item => { + // Check if this item has a toggle button (means it has children) + const toggleButton = item.querySelector('.o_toggle_fold'); + + // Only make bold if it has a toggle button (has children) + if (toggleButton && toggleButton.querySelector('i.fa-caret-down, i.fa-caret-right')) { + // Add bold class + item.classList.add('has-children-bold'); + + // Also add to header and label + const header = item.querySelector('header'); + const label = item.querySelector('.o_search_panel_label_title'); + + if (header) header.classList.add('has-children-bold'); + if (label) label.classList.add('has-children-bold'); + } else { + // Remove bold class if it doesn't have children + item.classList.remove('has-children-bold'); + const header = item.querySelector('header'); + const label = item.querySelector('.o_search_panel_label_title'); + + if (header) header.classList.remove('has-children-bold'); + if (label) label.classList.remove('has-children-bold'); + } + }); + }, +}); diff --git a/dev_odex30_accounting/account_chart_of_accounts/static/src/scss/account_hierarchy.scss b/dev_odex30_accounting/account_chart_of_accounts/static/src/scss/account_hierarchy.scss index bb42e73..94adca9 100644 --- a/dev_odex30_accounting/account_chart_of_accounts/static/src/scss/account_hierarchy.scss +++ b/dev_odex30_accounting/account_chart_of_accounts/static/src/scss/account_hierarchy.scss @@ -1,5 +1,6 @@ -.o_search_panel.account_account { - .o_search_panel_field.parent_id { +// CRITICAL: HTML uses class "account_root" not "account_account" +.o_search_panel.account_root { + .o_search_panel_field { .o_search_panel_category_value { .o_toggle_fold { margin-right: 15px; @@ -11,9 +12,9 @@ align-items: center; padding: 3px 8px; - &:hover { - color: #f0f0f0; - } +// &:hover { +// color: #f0f0f0; +// } .o_search_panel_label_title { flex: 1; @@ -25,15 +26,64 @@ } } - // Levels - &[data-level="0"] { - font-weight: bold; + // ONLY use JavaScript-added class (not :has() selector) + // Only accounts with actual toggle buttons get this class + &.has-children-bold { + font-weight: 900 !important; + + header, + .o_search_panel_label_title { + font-weight: 900 !important; + } } - &[data-level="1"] header { padding-left: 20px; } - &[data-level="2"] header { padding-left: 35px; } - &[data-level="3"] header { padding-left: 50px; } - &[data-level="4"] header { padding-left: 65px; } + // Levels indentation + &[data-level="1"] header { + padding-left: 20px; + } + + &[data-level="2"] header { + padding-left: 35px; + } + + &[data-level="3"] header { + padding-left: 50px; + } + + &[data-level="4"] header { + padding-left: 65px; + } + + &[data-level="5"] header { + padding-left: 80px; + } + + &[data-level="6"] header { + padding-left: 95px; + } } } } + +// Make view accounts bold - using custom class from JavaScript +.o_list_view { + + tr.o_data_row.account-view-type, + tr.o_data_row.fw-bold { + font-weight: 900 !important; + + td.o_data_cell { + font-weight: 900 !important; + // Add text shadow to make it appear even bolder + // Slightly darker color for more contrast + color: #2c3e50 !important; + } + + // Optional: Add background color like Odoo 14 + // Uncomment the lines below if you want a background color + // background-color: #f5f5f5 !important; + // &:hover { + // background-color: #ebebeb !important; + // } + } +} \ No newline at end of file diff --git a/dev_odex30_accounting/account_chart_of_accounts/views/account_account_view.xml b/dev_odex30_accounting/account_chart_of_accounts/views/account_account_view.xml index 2565afb..8bdeac0 100644 --- a/dev_odex30_accounting/account_chart_of_accounts/views/account_account_view.xml +++ b/dev_odex30_accounting/account_chart_of_accounts/views/account_account_view.xml @@ -75,6 +75,11 @@ + + + + account_type_field + diff --git a/dev_odex30_accounting/accountant/__init__.py b/dev_odex30_accounting/accountant/__init__.py new file mode 100644 index 0000000..9067a53 --- /dev/null +++ b/dev_odex30_accounting/accountant/__init__.py @@ -0,0 +1,67 @@ +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +import logging + +from . import models + +_logger = logging.getLogger(__name__) + + +def _accounting_post_init(env): + country_code = env.company.country_id.code + if country_code: + module_list = [] + + if country_code in ('AU', 'CA', 'US'): + module_list.append('account_reports_cash_basis') + + module_ids = env['ir.module.module'].search([('name', 'in', module_list), ('state', '=', 'uninstalled')]) + if module_ids: + module_ids.sudo().button_install() + + +def uninstall_hook(env): + try: + group_user = env.ref("account.group_account_user") + group_user.write({ + 'name': "Show Full Accounting Features", + 'implied_ids': [(3, env.ref('account.group_account_invoice').id)], + 'category_id': env.ref("base.module_category_hidden").id, + }) + group_readonly = env.ref("account.group_account_readonly") + group_readonly.write({ + 'name': "Show Full Accounting Features - Readonly", + 'category_id': env.ref("base.module_category_hidden").id, + }) + except ValueError as e: + _logger.warning(e) + + try: + group_manager = env.ref("account.group_account_manager") + group_manager.write({'name': "Billing Manager", + 'implied_ids': [(4, env.ref("account.group_account_invoice").id), + (3, env.ref("account.group_account_readonly").id), + (3, env.ref("account.group_account_user").id)]}) + except ValueError as e: + _logger.warning(e) + + # make the account_accountant features disappear (magic) + env.ref("account.group_account_user").write({'users': [(5, False, False)]}) + env.ref("account.group_account_readonly").write({'users': [(5, False, False)]}) + + # this menu should always be there, as the module depends on account. + # if it's not, there is something wrong with the db that should be investigated. + invoicing_menu = env.ref("account.menu_finance") + menus_to_move = [ + "account.menu_finance_receivables", + "account.menu_finance_payables", + "account.menu_finance_entries", + "account.menu_finance_reports", + "account.menu_finance_configuration", + "account.menu_board_journal_1", + ] + for menu_xmlids in menus_to_move: + try: + env.ref(menu_xmlids).parent_id = invoicing_menu + except ValueError as e: + _logger.warning(e) diff --git a/dev_odex30_accounting/accountant/__manifest__.py b/dev_odex30_accounting/accountant/__manifest__.py new file mode 100644 index 0000000..676fd80 --- /dev/null +++ b/dev_odex30_accounting/accountant/__manifest__.py @@ -0,0 +1,37 @@ +# Part of Odoo. See LICENSE file for full copyright and licensing details. +{ + 'name': 'Accounting', + 'version': '1.1', + 'category': 'Accounting/Accounting', + 'sequence': 30, + 'summary': 'Manage financial and analytic accounting', + 'description': """ +Accounting Access Rights +======================== +It gives the Administrator user access to all accounting features such as journal items and the chart of accounts. + +It assigns manager and user access rights to the Administrator for the accounting application and only user rights to the Demo user. +""", + 'website': 'https://www.odoo.com/app/accounting', + 'depends': ['odex30_account_accountant'], + 'data': [ + 'data/account_accountant_data.xml', + 'security/accounting_security.xml', + 'views/res_config_settings.xml', + 'views/partner_views.xml', + ], + 'demo': ['demo/account_accountant_demo.xml'], + 'installable': True, + 'application': True, + 'post_init_hook': '_accounting_post_init', + 'uninstall_hook': "uninstall_hook", + 'license': 'OEEL-1', + 'assets': { + 'web.assets_backend': [ + 'accountant/static/src/js/tours/accountant.js', + ], + 'web.assets_tests': [ + 'accountant/static/tests/tours/*', + ], + } +} diff --git a/dev_odex30_accounting/accountant/data/account_accountant_data.xml b/dev_odex30_accounting/accountant/data/account_accountant_data.xml new file mode 100644 index 0000000..fbc7362 --- /dev/null +++ b/dev_odex30_accounting/accountant/data/account_accountant_data.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dev_odex30_accounting/accountant/demo/account_accountant_demo.xml b/dev_odex30_accounting/accountant/demo/account_accountant_demo.xml new file mode 100644 index 0000000..b5e67c6 --- /dev/null +++ b/dev_odex30_accounting/accountant/demo/account_accountant_demo.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/dev_odex30_accounting/accountant/i18n/accountant.pot b/dev_odex30_accounting/accountant/i18n/accountant.pot new file mode 100644 index 0000000..ff8a02c --- /dev/null +++ b/dev_odex30_accounting/accountant/i18n/accountant.pot @@ -0,0 +1,28 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * accountant +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 18.0+e\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-12-19 09:51+0000\n" +"PO-Revision-Date: 2024-12-19 09:51+0000\n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: accountant +#: model:ir.ui.menu,name:accountant.menu_accounting +#: model_terms:ir.ui.view,arch_db:accountant.res_config_settings_view_form +#: model_terms:ir.ui.view,arch_db:accountant.res_partner_view_form +msgid "Accounting" +msgstr "" + +#. module: accountant +#: model:ir.model,name:accountant.model_account_move +msgid "Journal Entry" +msgstr "" diff --git a/dev_odex30_accounting/accountant/i18n/ar.po b/dev_odex30_accounting/accountant/i18n/ar.po new file mode 100644 index 0000000..eec72f5 --- /dev/null +++ b/dev_odex30_accounting/accountant/i18n/ar.po @@ -0,0 +1,32 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * accountant +# +# Translators: +# Wil Odoo, 2024 +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 18.0+e\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-12-19 09:51+0000\n" +"PO-Revision-Date: 2024-09-25 09:44+0000\n" +"Last-Translator: Wil Odoo, 2024\n" +"Language-Team: Arabic (https://app.transifex.com/odoo/teams/41243/ar/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Language: ar\n" +"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n" + +#. module: accountant +#: model:ir.ui.menu,name:accountant.menu_accounting +#: model_terms:ir.ui.view,arch_db:accountant.res_config_settings_view_form +#: model_terms:ir.ui.view,arch_db:accountant.res_partner_view_form +msgid "Accounting" +msgstr "المحاسبة " + +#. module: accountant +#: model:ir.model,name:accountant.model_account_move +msgid "Journal Entry" +msgstr "قيد اليومية" diff --git a/dev_odex30_accounting/accountant/models/__init__.py b/dev_odex30_accounting/accountant/models/__init__.py new file mode 100644 index 0000000..9c0a421 --- /dev/null +++ b/dev_odex30_accounting/accountant/models/__init__.py @@ -0,0 +1 @@ +from . import account_move diff --git a/dev_odex30_accounting/accountant/models/account_move.py b/dev_odex30_accounting/accountant/models/account_move.py new file mode 100644 index 0000000..b31f91a --- /dev/null +++ b/dev_odex30_accounting/accountant/models/account_move.py @@ -0,0 +1,9 @@ +from odoo import models + + +class AccountMove(models.Model): + _inherit = 'account.move' + + def _get_invoice_in_payment_state(self): + # OVERRIDE to enable the 'in_payment' state on invoices. + return 'in_payment' diff --git a/dev_odex30_accounting/accountant/security/accounting_security.xml b/dev_odex30_accounting/accountant/security/accounting_security.xml new file mode 100644 index 0000000..ae6944f --- /dev/null +++ b/dev_odex30_accounting/accountant/security/accounting_security.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + Accounting + Helps you handle your invoices and accounting actions. + + Invoicing: Invoices, payments and basic invoice reporting. + Invoicing & Banks: adds the accounting dashboard, bank management and follow-up reports. + Bookkeeper: access to all Accounting features, including reporting, asset management, analytic accounting, without configuration rights. + Administrator: full access including configuration rights and accounting data management. + Readonly: access to all the accounting data but in readonly mode, no actions allowed. + + + + + Read-only + + + + + Bookkeeper + + + + + + + + diff --git a/dev_odex30_accounting/accountant/static/description/icon.png b/dev_odex30_accounting/accountant/static/description/icon.png new file mode 100644 index 0000000..e64a0f2 Binary files /dev/null and b/dev_odex30_accounting/accountant/static/description/icon.png differ diff --git a/dev_odex30_accounting/accountant/static/description/icon.svg b/dev_odex30_accounting/accountant/static/description/icon.svg new file mode 100644 index 0000000..bf4af25 --- /dev/null +++ b/dev_odex30_accounting/accountant/static/description/icon.svg @@ -0,0 +1 @@ + diff --git a/dev_odex30_accounting/accountant/static/src/js/tours/accountant.js b/dev_odex30_accounting/accountant/static/src/js/tours/accountant.js new file mode 100644 index 0000000..cc316e8 --- /dev/null +++ b/dev_odex30_accounting/accountant/static/src/js/tours/accountant.js @@ -0,0 +1,12 @@ +/** @odoo-module **/ + +import { patch } from "@web/core/utils/patch"; +import { accountTourSteps } from "@account/js/tours/account"; +import { stepUtils } from "@web_tour/tour_service/tour_utils"; + + +patch(accountTourSteps, { + goToAccountMenu(description="Open Accounting Menu") { + return stepUtils.goToAppSteps('accountant.menu_accounting', description); + }, +}); diff --git a/dev_odex30_accounting/accountant/static/tests/tours/account_merge_wizard_tour.js b/dev_odex30_accounting/accountant/static/tests/tours/account_merge_wizard_tour.js new file mode 100644 index 0000000..0b338d8 --- /dev/null +++ b/dev_odex30_accounting/accountant/static/tests/tours/account_merge_wizard_tour.js @@ -0,0 +1,61 @@ +/** @odoo-module */ + +import { accountTourSteps } from "@account/js/tours/account"; +import { registry } from "@web/core/registry"; + +registry.category("web_tour.tours").add("account_merge_wizard_tour", { + url: "/odoo", + steps: () => [ + ...accountTourSteps.goToAccountMenu("Go to Accounting"), + { + content: "Go to Configuration", + trigger: 'span:contains("Configuration")', + run: "click", + }, + { + content: "Go to Chart of Accounts", + trigger: 'a:contains("Chart of Accounts")', + run: "click", + }, + { + trigger: '.o_breadcrumb .text-truncate:contains("Chart of Accounts")', + }, + { + content: "Select accounts", + trigger: "thead .o_list_record_selector", + run: "click", + }, + { + content: "Check that exactly 4 accounts are present and selected", + trigger: ".o_list_selection_box:contains(4):contains(selected)", + }, + { + content: "Open Actions menu", + trigger: ".o_cp_action_menus .dropdown-toggle", + run: "click", + }, + { + content: "Open Merge accounts wizard", + trigger: 'span:contains("Merge accounts")', + run: "click", + }, + { + content: "Group by name", + trigger: 'div[name="is_group_by_name"] input', + run: "click", + }, + { + content: "Wait for content to be updated", + trigger: 'td:contains("Current Assets (Current Assets)")', + }, + { + content: "Merge accounts", + trigger: 'button:not([disabled]) span:contains("Merge")', + run: "click", + }, + { + content: "Check that there are now exactly 2 accounts", + trigger: ".o_pager_limit:contains(2)", + }, + ], +}); diff --git a/dev_odex30_accounting/accountant/static/tests/tours/account_tours.js b/dev_odex30_accounting/accountant/static/tests/tours/account_tours.js new file mode 100644 index 0000000..aec8e4f --- /dev/null +++ b/dev_odex30_accounting/accountant/static/tests/tours/account_tours.js @@ -0,0 +1,10 @@ +/** @odoo-module **/ +import { patch } from "@web/core/utils/patch"; +import { accountTourSteps } from "@account/js/tours/account"; +import { stepUtils } from "@web_tour/tour_service/tour_utils"; + +patch(accountTourSteps, { + goToAccountMenu(description="Open Accounting Menu") { + return stepUtils.goToAppSteps('accountant.menu_accounting', description); + } +}); diff --git a/dev_odex30_accounting/accountant/tests/__init__.py b/dev_odex30_accounting/accountant/tests/__init__.py new file mode 100644 index 0000000..f49429e --- /dev/null +++ b/dev_odex30_accounting/accountant/tests/__init__.py @@ -0,0 +1 @@ +from . import test_tour diff --git a/dev_odex30_accounting/accountant/tests/test_tour.py b/dev_odex30_accounting/accountant/tests/test_tour.py new file mode 100644 index 0000000..3033b44 --- /dev/null +++ b/dev_odex30_accounting/accountant/tests/test_tour.py @@ -0,0 +1,48 @@ +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +import odoo.tests + +from odoo import Command +from odoo.addons.account.tests.common import AccountTestInvoicingHttpCommon + + +@odoo.tests.tagged('post_install_l10n', 'post_install', '-at_install') +class TestAccountantTours(AccountTestInvoicingHttpCommon): + def test_account_merge_wizard_tour(self): + companies = self.env['res.company'].create([ + {'name': 'tour_company_1'}, + {'name': 'tour_company_2'}, + ]) + + self.env['account.account'].create([ + { + 'company_ids': [Command.set(companies[0].ids)], + 'code': "100001", + 'name': "Current Assets", + 'account_type': 'asset_current', + }, + { + 'company_ids': [Command.set(companies[0].ids)], + 'code': "100002", + 'name': "Non-Current Assets", + 'account_type': 'asset_non_current', + }, + { + 'company_ids': [Command.set(companies[1].ids)], + 'code': "200001", + 'name': "Current Assets", + 'account_type': 'asset_current', + }, + { + 'company_ids': [Command.set(companies[1].ids)], + 'code': "200002", + 'name': "Non-Current Assets", + 'account_type': 'asset_non_current', + }, + ]) + + self.env.ref('base.user_admin').write({ + 'company_id': companies[0].id, + 'company_ids': [Command.set(companies.ids)], + }) + self.start_tour("/odoo", 'account_merge_wizard_tour', login="admin", cookies={"cids": f"{companies[0].id}-{companies[1].id}"}) diff --git a/dev_odex30_accounting/accountant/views/partner_views.xml b/dev_odex30_accounting/accountant/views/partner_views.xml new file mode 100644 index 0000000..8058394 --- /dev/null +++ b/dev_odex30_accounting/accountant/views/partner_views.xml @@ -0,0 +1,16 @@ + + + + res.partner.form + res.partner + + + + Accounting + + + Accounting + + + + diff --git a/dev_odex30_accounting/accountant/views/res_config_settings.xml b/dev_odex30_accounting/accountant/views/res_config_settings.xml new file mode 100644 index 0000000..fcd7ee4 --- /dev/null +++ b/dev_odex30_accounting/accountant/views/res_config_settings.xml @@ -0,0 +1,15 @@ + + + + res.config.settings.view.form.inherit.accountant + res.config.settings + + + + Accounting + Accounting + /accountant/static/description/icon.png + + + + diff --git a/dev_odex30_accounting/odex30_account_online_sync/__init__.py b/dev_odex30_accounting/odex30_account_online_sync/__init__.py deleted file mode 100644 index bbc5580..0000000 --- a/dev_odex30_accounting/odex30_account_online_sync/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# -*- coding: utf-8 -*- - -from . import controllers -from . import models -from . import wizard diff --git a/dev_odex30_accounting/odex30_account_online_sync/__manifest__.py b/dev_odex30_accounting/odex30_account_online_sync/__manifest__.py deleted file mode 100644 index 5946b4a..0000000 --- a/dev_odex30_accounting/odex30_account_online_sync/__manifest__.py +++ /dev/null @@ -1,51 +0,0 @@ -# -*- coding: utf-8 -*- -{ - 'name': "Online Bank Statement Synchronization", - 'summary': """ - This module is used for Online bank synchronization.""", - - 'description': """ - This module is used for Online bank synchronization. It provides basic methods to synchronize bank statement. - """, - 'author': "Expert Co. Ltd.", - 'website': "http://www.exp-sa.com", - 'category': 'Odex30-Accounting/Odex30-Accounting', - 'version': '2.0', - 'depends': ['odex30_account_accountant'], - - 'data': [ - 'data/config_parameter.xml', - 'data/ir_cron.xml', - 'data/mail_activity_type_data.xml', - 'data/sync_reminder_email_template.xml', - - 'security/ir.model.access.csv', - 'security/account_online_sync_security.xml', - - 'views/account_online_sync_views.xml', - 'views/account_bank_statement_view.xml', - 'views/account_journal_view.xml', - 'views/account_online_sync_portal_templates.xml', - 'views/account_journal_dashboard_view.xml', - - 'wizard/account_bank_selection_wizard.xml', - 'wizard/account_journal_missing_transactions.xml', - 'wizard/account_journal_duplicate_transactions.xml', - 'wizard/account_bank_statement_line.xml', - ], - 'license': 'OEEL-1', - 'auto_install': True, - 'assets': { - 'web.assets_backend': [ - 'odex30_account_online_sync/static/src/components/**/*', - 'odex30_account_online_sync/static/src/js/odoo_fin_connector.js', - ], - 'web.assets_frontend': [ - 'odex30_account_online_sync/static/src/js/online_sync_portal.js', - ], - 'web.qunit_suite_tests': [ - 'odex30_account_online_sync/static/tests/helpers/*.js', - 'odex30_account_online_sync/static/tests/*.js', - ], - } -} diff --git a/dev_odex30_accounting/odex30_account_online_sync/__pycache__/__init__.cpython-311.pyc b/dev_odex30_accounting/odex30_account_online_sync/__pycache__/__init__.cpython-311.pyc deleted file mode 100644 index e37619d..0000000 Binary files a/dev_odex30_accounting/odex30_account_online_sync/__pycache__/__init__.cpython-311.pyc and /dev/null differ diff --git a/dev_odex30_accounting/odex30_account_online_sync/controllers/__init__.py b/dev_odex30_accounting/odex30_account_online_sync/controllers/__init__.py deleted file mode 100644 index 8c3feb6..0000000 --- a/dev_odex30_accounting/odex30_account_online_sync/controllers/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from . import portal diff --git a/dev_odex30_accounting/odex30_account_online_sync/controllers/__pycache__/__init__.cpython-311.pyc b/dev_odex30_accounting/odex30_account_online_sync/controllers/__pycache__/__init__.cpython-311.pyc deleted file mode 100644 index 6439cb3..0000000 Binary files a/dev_odex30_accounting/odex30_account_online_sync/controllers/__pycache__/__init__.cpython-311.pyc and /dev/null differ diff --git a/dev_odex30_accounting/odex30_account_online_sync/controllers/__pycache__/portal.cpython-311.pyc b/dev_odex30_accounting/odex30_account_online_sync/controllers/__pycache__/portal.cpython-311.pyc deleted file mode 100644 index 28f9ba1..0000000 Binary files a/dev_odex30_accounting/odex30_account_online_sync/controllers/__pycache__/portal.cpython-311.pyc and /dev/null differ diff --git a/dev_odex30_accounting/odex30_account_online_sync/controllers/portal.py b/dev_odex30_accounting/odex30_account_online_sync/controllers/portal.py deleted file mode 100644 index 9556ea4..0000000 --- a/dev_odex30_accounting/odex30_account_online_sync/controllers/portal.py +++ /dev/null @@ -1,58 +0,0 @@ -import json - -from odoo import http -from odoo.http import request -from odoo.addons.portal.controllers.portal import CustomerPortal -from odoo.tools import format_amount, format_date -from odoo.exceptions import AccessError, MissingError, UserError - - -class OnlineSynchronizationPortal(CustomerPortal): - - @http.route(['/renew_consent/'], type='http', auth="public", website=True, sitemap=False) - def portal_online_sync_renew_consent(self, journal_id, access_token=None, **kw): - # Display a page to the user allowing to renew the consent for his bank sync. - # Requires the same rights as the button in odoo. - try: - journal_sudo = self._document_check_access('account.journal', journal_id, access_token) - except (AccessError, MissingError): - return request.redirect('/my') - values = self._prepare_portal_layout_values() - # Ignore the route if the journal isn't one using bank sync. - if not journal_sudo.account_online_account_id: - raise request.not_found() - - balance = journal_sudo.account_online_account_id.balance - if journal_sudo.account_online_account_id.currency_id: - formatted_balance = format_amount(request.env, balance, journal_sudo.account_online_account_id.currency_id) - else: - formatted_balance = format_amount(request.env, balance, journal_sudo.currency_id or journal_sudo.company_id.currency_id) - - values.update({ - 'bank': journal_sudo.bank_account_id.bank_name or journal_sudo.account_online_account_id.name, - 'bank_account': journal_sudo.bank_account_id.acc_number, - 'journal': journal_sudo.name, - 'latest_balance_formatted': formatted_balance, - 'latest_balance': balance, - 'latest_sync': format_date(request.env, journal_sudo.account_online_account_id.last_sync, date_format="MMM dd, YYYY"), - 'iframe_params': json.dumps(journal_sudo.action_extend_consent()), - }) - return request.render("account_online_synchronization.portal_renew_consent", values) - - - @http.route(['/renew_consent//complete'], type='http', auth="public", methods=['POST'], website=True) - def portal_online_sync_action_complete(self, journal_id, access_token=None, **kw): - # Complete the consent renewal process - try: - journal_sudo = self._document_check_access('account.journal', journal_id, access_token) - except (AccessError, MissingError): - return request.redirect('/my') - # Ignore the route if the journal isn't one using bank sync. - if not journal_sudo.account_online_link_id: - raise request.not_found() - try: - journal_sudo.account_online_link_id._update_connection_status() - journal_sudo.manual_sync() - except UserError: - pass - return request.make_response(json.dumps({'status': 'done'})) diff --git a/dev_odex30_accounting/odex30_account_online_sync/data/config_parameter.xml b/dev_odex30_accounting/odex30_account_online_sync/data/config_parameter.xml deleted file mode 100644 index 5a8a8fa..0000000 --- a/dev_odex30_accounting/odex30_account_online_sync/data/config_parameter.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - odex30_account_online_sync.proxy_mode - production - - - odex30_account_online_sync.request_timeout - 60 - - - diff --git a/dev_odex30_accounting/odex30_account_online_sync/data/ir_cron.xml b/dev_odex30_accounting/odex30_account_online_sync/data/ir_cron.xml deleted file mode 100644 index 5ed932b..0000000 --- a/dev_odex30_accounting/odex30_account_online_sync/data/ir_cron.xml +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - Account: Journal online sync - - code - model._cron_fetch_online_transactions() - - - 12 - hours - - - Account: Journal online Waiting Synchronization - - code - model._cron_fetch_waiting_online_transactions() - - - 5 - minutes - - - - Account: Journal online sync reminder - - code - model._cron_send_reminder_email() - - - 1 - days - - - - Account: Journal online sync cleanup unused connections - - code - model._cron_delete_unused_connection() - - - 1 - days - - - diff --git a/dev_odex30_accounting/odex30_account_online_sync/data/mail_activity_type_data.xml b/dev_odex30_accounting/odex30_account_online_sync/data/mail_activity_type_data.xml deleted file mode 100644 index 0a96bcc..0000000 --- a/dev_odex30_accounting/odex30_account_online_sync/data/mail_activity_type_data.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - Bank Synchronization: Update consent - fa-university - warning - account.journal - 0 - - - - Consent Renewal - - - account.journal - - - - - diff --git a/dev_odex30_accounting/odex30_account_online_sync/data/neutralize.sql b/dev_odex30_accounting/odex30_account_online_sync/data/neutralize.sql deleted file mode 100644 index 465aaa7..0000000 --- a/dev_odex30_accounting/odex30_account_online_sync/data/neutralize.sql +++ /dev/null @@ -1,4 +0,0 @@ --- disable bank synchronisation links -UPDATE account_online_link - SET provider_data = '', - client_id = 'duplicate'; diff --git a/dev_odex30_accounting/odex30_account_online_sync/data/sync_reminder_email_template.xml b/dev_odex30_accounting/odex30_account_online_sync/data/sync_reminder_email_template.xml deleted file mode 100644 index abf2b28..0000000 --- a/dev_odex30_accounting/odex30_account_online_sync/data/sync_reminder_email_template.xml +++ /dev/null @@ -1,71 +0,0 @@ - - - - - Bank connection expiration reminder - Your bank connection is expiring soon - {{ object.company_id.email_formatted or user.email_formatted }} - {{ object.renewal_contact_email }} - - - - - - - - - - - -
- - - - - - - -
- - - - - - - -
-
- Hello,

- The connection between https://yourcompany.odoo.com and Belfius expired.expires in 10 days.
- - Security Tip: Check that the domain name you are redirected to is: https://yourcompany.odoo.com -
-
-
-
-
-
- - - - -
- Powered by Odoo -
- - - - -
- PS: This is an automated email sent by Odoo Accounting to remind you before a bank sync consent expiration. -
-
-
-
-
-
diff --git a/dev_odex30_accounting/odex30_account_online_sync/i18n/account_online_synchronization.pot b/dev_odex30_accounting/odex30_account_online_sync/i18n/account_online_synchronization.pot deleted file mode 100644 index b437820..0000000 --- a/dev_odex30_accounting/odex30_account_online_sync/i18n/account_online_synchronization.pot +++ /dev/null @@ -1,1322 +0,0 @@ -# Translation of Odoo Server. -# This file contains the translation of the following modules: -# * odex30_account_online_sync -# -msgid "" -msgstr "" -"Project-Id-Version: Odoo Server 18.0+e\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-07-11 18:50+0000\n" -"PO-Revision-Date: 2025-07-11 18:50+0000\n" -"Last-Translator: \n" -"Language-Team: \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: \n" -"Plural-Forms: \n" - -#. module: odex30_account_online_sync -#. odoo-python -#: code:addons/odex30_account_online_sync/models/account_online.py:0 -msgid "" -"\n" -"\n" -"If you've already opened a ticket for this issue, don't report it again: a support agent will contact you shortly." -msgstr "" - -#. module: odex30_account_online_sync -#. odoo-python -#: code:addons/odex30_account_online_sync/wizard/account_journal_duplicate_transactions.py:0 -msgid "%s duplicate transactions" -msgstr "" - -#. module: odex30_account_online_sync -#. odoo-javascript -#: code:addons/odex30_account_online_sync/static/src/components/transient_bank_statement_line_list_view/transient_bank_statement_line_list_view.xml:0 -msgid "" -").\n" -" This might cause duplicate entries." -msgstr "" - -#. module: odex30_account_online_sync -#. odoo-javascript -#: code:addons/odex30_account_online_sync/static/src/components/refresh_spin_journal_widget/refresh_spin_journal_widget.xml:0 -msgid "0 transaction fetched" -msgstr "" - -#. module: odex30_account_online_sync -#: model_terms:ir.ui.view,arch_db:account_online_synchronization.account_duplicate_transaction_wizard_view_form -msgid " Delete Selected" -msgstr "" - -#. module: odex30_account_online_sync -#: model_terms:ir.ui.view,arch_db:account_online_synchronization.account_journal_form -msgid " Send Now" -msgstr "" - -#. module: odex30_account_online_sync -#: model:mail.template,body_html:account_online_synchronization.email_template_sync_reminder -msgid "" -"\n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -"
\n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -"
\n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -"
\n" -"
\n" -" Hello,

\n" -" The connection between https://yourcompany.odoo.com and Belfius expired.expires in 10 days.
\n" -" \n" -" Security Tip: Check that the domain name you are redirected to is: https://yourcompany.odoo.com\n" -"
\n" -"
\n" -"
\n" -"
\n" -"
\n" -"
\n" -" \n" -" \n" -" \n" -" \n" -"
\n" -" Powered by Odoo\n" -"
\n" -" \n" -" \n" -" \n" -" \n" -"
\n" -" PS: This is an automated email sent by Odoo Accounting to remind you before a bank sync consent expiration.\n" -"
\n" -"
\n" -" " -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_online_link__access_token -msgid "Access Token" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_online_account__account_data -msgid "Account Data" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_online_account__name -msgid "Account Name" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,help:account_online_synchronization.field_account_online_account__name -msgid "Account Name as provided by third party provider" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_bank_statement_line_transient__account_number -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_online_account__account_number -msgid "Account Number" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_bank_selection__account_online_account_ids -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_bank_statement_line_transient__online_account_id -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_journal__account_online_account_id -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_online_link__account_online_account_ids -msgid "Account Online Account" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_bank_selection__account_online_link_id -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_bank_statement_line__online_link_id -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_journal__account_online_link_id -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_online_account__account_online_link_id -msgid "Account Online Link" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.actions.server,name:account_online_synchronization.online_sync_cron_waiting_synchronization_ir_actions_server -msgid "Account: Journal online Waiting Synchronization" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.actions.server,name:account_online_synchronization.online_sync_cron_ir_actions_server -msgid "Account: Journal online sync" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.actions.server,name:account_online_synchronization.online_sync_unused_connection_cron_ir_actions_server -msgid "Account: Journal online sync cleanup unused connections" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.actions.server,name:account_online_synchronization.online_sync_mail_cron_ir_actions_server -msgid "Account: Journal online sync reminder" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_online_link__message_needaction -msgid "Action Needed" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_online_link__activity_ids -msgid "Activities" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_online_link__activity_exception_decoration -msgid "Activity Exception Decoration" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_online_link__activity_state -msgid "Activity State" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model,name:account_online_synchronization.model_mail_activity_type -msgid "Activity Type" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_online_link__activity_type_icon -msgid "Activity Type Icon" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_bank_statement_line_transient__amount -msgid "Amount" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_bank_statement_line_transient__amount_currency -msgid "Amount in Currency" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_online_link__message_attachment_count -msgid "Attachment Count" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_online_link__auto_sync -msgid "Automatic synchronization" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_online_account__balance -msgid "Balance" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,help:account_online_synchronization.field_account_online_account__balance -msgid "Balance of the account sent by the third party provider" -msgstr "" - -#. module: odex30_account_online_sync -#: model_terms:ir.ui.view,arch_db:account_online_synchronization.portal_renew_consent -msgid "Bank" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model,name:account_online_synchronization.model_account_online_link -msgid "Bank Connection" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model,name:account_online_synchronization.model_account_bank_statement_line -msgid "Bank Statement Line" -msgstr "" - -#. module: odex30_account_online_sync -#: model:mail.activity.type,name:account_online_synchronization.bank_sync_activity_update_consent -msgid "Bank Synchronization: Update consent" -msgstr "" - -#. module: odex30_account_online_sync -#. odoo-python -#: code:addons/odex30_account_online_sync/models/account_online.py:0 -msgid "Bank Synchronization: Update your consent" -msgstr "" - -#. module: odex30_account_online_sync -#: model:mail.template,name:account_online_synchronization.email_template_sync_reminder -msgid "Bank connection expiration reminder" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model,name:account_online_synchronization.model_bank_rec_widget -msgid "Bank reconciliation widget for a single statement line" -msgstr "" - -#. module: odex30_account_online_sync -#: model_terms:ir.ui.view,arch_db:account_online_synchronization.account_missing_transaction_wizard_view_form -#: model_terms:ir.ui.view,arch_db:account_online_synchronization.view_account_bank_selection_form_wizard -msgid "Cancel" -msgstr "" - -#. module: odex30_account_online_sync -#. odoo-python -#: code:addons/odex30_account_online_sync/models/account_online.py:0 -msgid "Check the documentation" -msgstr "" - -#. module: odex30_account_online_sync -#: model_terms:ir.ui.view,arch_db:account_online_synchronization.account_duplicate_transaction_wizard_view_form -msgid "" -"Choose a date and a journal from which you want to check the transactions." -msgstr "" - -#. module: odex30_account_online_sync -#: model_terms:ir.ui.view,arch_db:account_online_synchronization.account_missing_transaction_wizard_view_form -msgid "Choose a date and a journal from which you want to fetch transactions" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_online_link__client_id -msgid "Client" -msgstr "" - -#. module: odex30_account_online_sync -#: model_terms:ir.ui.view,arch_db:account_online_synchronization.account_online_link_view_form -msgid "Client id" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,help:account_online_synchronization.field_account_journal__renewal_contact_email -msgid "" -"Comma separated list of email addresses to send consent renewal " -"notifications 15, 3 and 1 days before expiry" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model,name:account_online_synchronization.model_res_company -msgid "Companies" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_bank_statement_line_transient__company_id -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_online_account__company_id -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_online_link__company_id -msgid "Company" -msgstr "" - -#. module: odex30_account_online_sync -#: model_terms:ir.ui.view,arch_db:account_online_synchronization.account_online_link_view_form -msgid "Connect" -msgstr "" - -#. module: odex30_account_online_sync -#: model_terms:ir.ui.view,arch_db:account_online_synchronization.view_account_bank_selection_form_wizard -msgid "Connect Bank" -msgstr "" - -#. module: odex30_account_online_sync -#: model_terms:ir.ui.view,arch_db:account_online_synchronization.account_journal_dashboard_inherit_online_sync -msgid "Connect bank" -msgstr "" - -#. module: odex30_account_online_sync -#: model_terms:ir.ui.view,arch_db:account_online_synchronization.portal_renew_consent -msgid "Connect my Bank" -msgstr "" - -#. module: odex30_account_online_sync -#: model_terms:ir.ui.view,arch_db:account_online_synchronization.portal_renew_consent -msgid "Connect your bank account to Odoo" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields.selection,name:account_online_synchronization.selection__account_online_link__state__connected -msgid "Connected" -msgstr "" - -#. module: odex30_account_online_sync -#. odoo-javascript -#: code:addons/odex30_account_online_sync/static/src/components/connected_until_widget/connected_until_widget.xml:0 -msgid "Connected until" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_journal__renewal_contact_email -msgid "Connection Requests" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_online_link__connection_state_details -msgid "Connection State Details" -msgstr "" - -#. module: odex30_account_online_sync -#: model:mail.message.subtype,name:account_online_synchronization.bank_sync_consent_renewal -msgid "Consent Renewal" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model,name:account_online_synchronization.model_res_partner -msgid "Contact" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_bank_selection__create_uid -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_bank_statement_line_transient__create_uid -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_duplicate_transaction_wizard__create_uid -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_missing_transaction_wizard__create_uid -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_online_account__create_uid -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_online_link__create_uid -msgid "Created by" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_bank_selection__create_date -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_bank_statement_line_transient__create_date -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_duplicate_transaction_wizard__create_date -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_missing_transaction_wizard__create_date -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_online_account__create_date -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_online_link__create_date -msgid "Created on" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_bank_statement_line_transient__currency_id -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_online_account__currency_id -msgid "Currency" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_bank_statement_line_transient__date -msgid "Date" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,help:account_online_synchronization.field_account_journal__expiring_synchronization_date -#: model:ir.model.fields,help:account_online_synchronization.field_account_online_link__expiring_synchronization_date -msgid "Date when the consent for this connection expires" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_bank_selection__display_name -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_bank_statement_line_transient__display_name -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_duplicate_transaction_wizard__display_name -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_missing_transaction_wizard__display_name -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_online_account__display_name -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_online_link__display_name -msgid "Display Name" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields.selection,name:account_online_synchronization.selection__account_online_account__fetching_status__done -msgid "Done" -msgstr "" - -#. module: odex30_account_online_sync -#. odoo-python -#: code:addons/odex30_account_online_sync/models/account_bank_statement.py:0 -#: model:ir.model.fields.selection,name:account_online_synchronization.selection__account_online_link__state__error -msgid "Error" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_journal__expiring_synchronization_date -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_online_link__expiring_synchronization_date -msgid "Expiring Synchronization Date" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_journal__expiring_synchronization_due_day -msgid "Expiring Synchronization Due Day" -msgstr "" - -#. module: odex30_account_online_sync -#. odoo-javascript -#: code:addons/odex30_account_online_sync/static/src/components/connected_until_widget/connected_until_widget.xml:0 -msgid "Extend Connection" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,help:account_online_synchronization.field_account_online_account__account_data -msgid "Extra information needed by third party provider" -msgstr "" - -#. module: odex30_account_online_sync -#: model_terms:ir.ui.view,arch_db:account_online_synchronization.account_missing_transaction_wizard_view_form -msgid "Fetch" -msgstr "" - -#. module: odex30_account_online_sync -#: model_terms:ir.ui.view,arch_db:account_online_synchronization.account_duplicate_transaction_wizard_view_form -#: model_terms:ir.ui.view,arch_db:account_online_synchronization.account_missing_transaction_wizard_view_form -msgid "Fetch Missing Bank Statements Wizard" -msgstr "" - -#. module: odex30_account_online_sync -#. odoo-javascript -#: code:addons/odex30_account_online_sync/static/src/components/refresh_spin_journal_widget/refresh_spin_journal_widget.xml:0 -#: model_terms:ir.ui.view,arch_db:account_online_synchronization.account_online_link_view_form -msgid "Fetch Transactions" -msgstr "" - -#. module: odex30_account_online_sync -#. odoo-python -#: code:addons/odex30_account_online_sync/models/account_online.py:0 -msgid "Fetched Transactions" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_journal__online_sync_fetching_status -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_online_account__fetching_status -msgid "Fetching Status" -msgstr "" - -#. module: odex30_account_online_sync -#. odoo-javascript -#: code:addons/odex30_account_online_sync/static/src/components/refresh_spin_journal_widget/refresh_spin_journal_widget.xml:0 -msgid "Fetching..." -msgstr "" - -#. module: odex30_account_online_sync -#. odoo-javascript -#. odoo-python -#: code:addons/odex30_account_online_sync/models/account_journal.py:0 -#: code:addons/odex30_account_online_sync/static/src/components/bank_reconciliation/find_duplicate_transactions_cog_menu.xml:0 -msgid "Find Duplicate Transactions" -msgstr "" - -#. module: odex30_account_online_sync -#. odoo-javascript -#. odoo-python -#: code:addons/odex30_account_online_sync/models/account_journal.py:0 -#: code:addons/odex30_account_online_sync/static/src/components/bank_reconciliation/fetch_missing_transactions_cog_menu.xml:0 -msgid "Find Missing Transactions" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_duplicate_transaction_wizard__first_ids_in_group -msgid "First Ids In Group" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_online_link__message_follower_ids -msgid "Followers" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_online_link__message_partner_ids -msgid "Followers (Partners)" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,help:account_online_synchronization.field_account_online_link__activity_type_icon -msgid "Font awesome icon e.g. fa-tasks" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_bank_statement_line_transient__foreign_currency_id -msgid "Foreign Currency" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_online_link__has_message -msgid "Has Message" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_online_link__has_unlinked_accounts -msgid "Has Unlinked Accounts" -msgstr "" - -#. module: odex30_account_online_sync -#. odoo-python -#: code:addons/odex30_account_online_sync/models/account_online.py:0 -msgid "Here" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_bank_selection__id -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_bank_statement_line_transient__id -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_duplicate_transaction_wizard__id -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_missing_transaction_wizard__id -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_online_account__id -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_online_link__id -msgid "ID" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_online_link__activity_exception_icon -msgid "Icon" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,help:account_online_synchronization.field_account_online_link__activity_exception_icon -msgid "Icon to indicate an exception activity." -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,help:account_online_synchronization.field_account_online_account__online_identifier -msgid "Id used to identify account by third party provider" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,help:account_online_synchronization.field_account_online_link__message_needaction -msgid "If checked, new messages require your attention." -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,help:account_online_synchronization.field_account_online_link__message_has_error -#: model:ir.model.fields,help:account_online_synchronization.field_account_online_link__message_has_sms_error -msgid "If checked, some messages have a delivery error." -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,help:account_online_synchronization.field_account_online_account__inverse_balance_sign -msgid "If checked, the balance sign will be inverted" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,help:account_online_synchronization.field_account_online_account__inverse_transaction_sign -msgid "If checked, the transaction sign will be inverted" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,help:account_online_synchronization.field_account_online_link__auto_sync -msgid "" -"If possible, we will try to automatically fetch new transactions for this record\n" -" \n" -"If the automatic sync is disabled. that will be due to security policy on the bank's end. So, they have to launch the sync manually" -msgstr "" - -#. module: odex30_account_online_sync -#. odoo-javascript -#: code:addons/odex30_account_online_sync/static/src/components/transient_bank_statement_line_list_view/transient_bank_statement_line_list_view.xml:0 -msgid "Import Transactions" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,help:account_online_synchronization.field_account_online_link__provider_data -msgid "Information needed to interact with third party provider" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,help:account_online_synchronization.field_account_bank_selection__institution_name -#: model:ir.model.fields,help:account_online_synchronization.field_account_online_link__name -msgid "Institution Name" -msgstr "" - -#. module: odex30_account_online_sync -#. odoo-python -#: code:addons/odex30_account_online_sync/models/account_online.py:0 -msgid "Internal Error" -msgstr "" - -#. module: odex30_account_online_sync -#. odoo-python -#: code:addons/odex30_account_online_sync/models/account_online.py:0 -msgid "Invalid URL" -msgstr "" - -#. module: odex30_account_online_sync -#. odoo-python -#: code:addons/odex30_account_online_sync/models/account_online.py:0 -msgid "Invalid value for proxy_mode config parameter." -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_online_account__inverse_balance_sign -msgid "Inverse Balance Sign" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_online_account__inverse_transaction_sign -msgid "Inverse Transaction Sign" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_online_link__message_is_follower -msgid "Is Follower" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model,name:account_online_synchronization.model_account_journal -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_bank_statement_line_transient__journal_id -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_duplicate_transaction_wizard__journal_id -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_missing_transaction_wizard__journal_id -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_online_account__journal_ids -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_online_link__journal_ids -#: model_terms:ir.ui.view,arch_db:account_online_synchronization.portal_renew_consent -msgid "Journal" -msgstr "" - -#. module: odex30_account_online_sync -#. odoo-python -#: code:addons/odex30_account_online_sync/models/account_online.py:0 -msgid "" -"Journal %(journal_name)s has been set up with a different currency and " -"already has existing entries. You can't link selected bank account in " -"%(currency_name)s to it" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_online_link__last_refresh -msgid "Last Refresh" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_bank_selection__write_uid -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_bank_statement_line_transient__write_uid -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_duplicate_transaction_wizard__write_uid -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_missing_transaction_wizard__write_uid -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_online_account__write_uid -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_online_link__write_uid -msgid "Last Updated by" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_bank_selection__write_date -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_bank_statement_line_transient__write_date -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_duplicate_transaction_wizard__write_date -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_missing_transaction_wizard__write_date -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_online_account__write_date -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_online_link__write_date -msgid "Last Updated on" -msgstr "" - -#. module: odex30_account_online_sync -#: model_terms:ir.ui.view,arch_db:account_online_synchronization.account_online_link_view_form -msgid "Last refresh" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_online_account__last_sync -msgid "Last synchronization" -msgstr "" - -#. module: odex30_account_online_sync -#: model_terms:ir.ui.view,arch_db:account_online_synchronization.portal_renew_consent -msgid "Latest Balance" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model,name:account_online_synchronization.model_account_bank_selection -msgid "Link a bank account to the selected journal" -msgstr "" - -#. module: odex30_account_online_sync -#. odoo-python -#: code:addons/odex30_account_online_sync/wizard/account_journal_missing_transactions.py:0 -msgid "Manual Bank Statement Lines" -msgstr "" - -#. module: odex30_account_online_sync -#. odoo-python -#: code:addons/odex30_account_online_sync/models/account_online.py:0 -msgid "Message" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_online_link__message_has_error -msgid "Message Delivery error" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_online_link__message_ids -msgid "Messages" -msgstr "" - -#. module: odex30_account_online_sync -#. odoo-python -#: code:addons/odex30_account_online_sync/wizard/account_journal_missing_transactions.py:0 -msgid "Missing and Pending Transactions" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_online_link__my_activity_date_deadline -msgid "My Activity Deadline" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_bank_selection__institution_name -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_online_link__name -msgid "Name" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_online_link__activity_calendar_event_id -msgid "Next Activity Calendar Event" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_online_link__activity_date_deadline -msgid "Next Activity Deadline" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_online_link__activity_summary -msgid "Next Activity Summary" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_online_link__activity_type_id -msgid "Next Activity Type" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_online_link__next_refresh -msgid "Next synchronization" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields.selection,name:account_online_synchronization.selection__account_online_link__state__disconnected -msgid "Not Connected" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_online_link__message_needaction_counter -msgid "Number of Actions" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_online_link__message_has_error_counter -msgid "Number of errors" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,help:account_online_synchronization.field_account_online_link__message_needaction_counter -msgid "Number of messages requiring action" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,help:account_online_synchronization.field_account_online_link__message_has_error_counter -msgid "Number of messages with delivery error" -msgstr "" - -#. module: odex30_account_online_sync -#: model_terms:ir.ui.view,arch_db:account_online_synchronization.portal_renew_consent -msgid "Odoo" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_bank_statement_line__online_account_id -msgid "Online Account" -msgstr "" - -#. module: odex30_account_online_sync -#: model_terms:ir.ui.view,arch_db:account_online_synchronization.account_online_link_view_form -msgid "Online Accounts" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_online_account__online_identifier -msgid "Online Identifier" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_journal__next_link_synchronization -msgid "Online Link Next synchronization" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_bank_statement_line__online_partner_information -#: model:ir.model.fields,field_description:account_online_synchronization.field_res_partner__online_partner_information -#: model:ir.model.fields,field_description:account_online_synchronization.field_res_users__online_partner_information -msgid "Online Partner Information" -msgstr "" - -#. module: odex30_account_online_sync -#. odoo-python -#: code:addons/odex30_account_online_sync/models/account_journal.py:0 -#: model:ir.actions.act_window,name:account_online_synchronization.action_account_online_link_form -#: model:ir.ui.menu,name:account_online_synchronization.menu_action_online_link_account -#: model_terms:ir.actions.act_window,help:account_online_synchronization.action_account_online_link_form -msgid "Online Synchronization" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_bank_statement_line__online_transaction_identifier -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_bank_statement_line_transient__online_transaction_identifier -msgid "Online Transaction Identifier" -msgstr "" - -#. module: odex30_account_online_sync -#. odoo-python -#: code:addons/odex30_account_online_sync/models/account_bank_statement.py:0 -msgid "Opening statement: first synchronization" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_bank_statement_line_transient__partner_name -msgid "Partner Name" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_bank_statement_line_transient__payment_ref -msgid "Payment Ref" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields.selection,name:account_online_synchronization.selection__account_bank_statement_line_transient__state__pending -msgid "Pending" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields.selection,name:account_online_synchronization.selection__account_online_account__fetching_status__planned -msgid "Planned" -msgstr "" - -#. module: odex30_account_online_sync -#. odoo-python -#: code:addons/odex30_account_online_sync/wizard/account_journal_missing_transactions.py:0 -msgid "Please enter a valid Starting Date to continue." -msgstr "" - -#. module: odex30_account_online_sync -#. odoo-python -#: code:addons/odex30_account_online_sync/models/account_online.py:0 -msgid "Please reconnect your online account." -msgstr "" - -#. module: odex30_account_online_sync -#. odoo-python -#: code:addons/odex30_account_online_sync/wizard/account_bank_statement_line.py:0 -msgid "Please select first the transactions you wish to import." -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields.selection,name:account_online_synchronization.selection__account_bank_statement_line_transient__state__posted -msgid "Posted" -msgstr "" - -#. module: odex30_account_online_sync -#: model_terms:ir.ui.view,arch_db:account_online_synchronization.missing_bank_statement_line_search -msgid "Posted Transactions" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields.selection,name:account_online_synchronization.selection__account_online_account__fetching_status__processing -msgid "Processing" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_online_link__provider_data -msgid "Provider Data" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_online_link__provider_type -msgid "Provider Type" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_online_link__rating_ids -msgid "Ratings" -msgstr "" - -#. module: odex30_account_online_sync -#: model_terms:ir.ui.view,arch_db:account_online_synchronization.account_online_link_view_form -msgid "Reconnect" -msgstr "" - -#. module: odex30_account_online_sync -#. odoo-javascript -#: code:addons/odex30_account_online_sync/static/src/components/connected_until_widget/connected_until_widget.xml:0 -#: code:addons/odex30_account_online_sync/static/src/components/refresh_spin_journal_widget/refresh_spin_journal_widget.xml:0 -#: model_terms:ir.ui.view,arch_db:account_online_synchronization.account_journal_dashboard_inherit_online_sync -msgid "Reconnect Bank" -msgstr "" - -#. module: odex30_account_online_sync -#. odoo-python -#: code:addons/odex30_account_online_sync/models/account_online.py:0 -msgid "Redirect" -msgstr "" - -#. module: odex30_account_online_sync -#. odoo-javascript -#: code:addons/odex30_account_online_sync/static/src/components/refresh_spin_journal_widget/refresh_spin_journal_widget.xml:0 -msgid "Refresh" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_online_link__refresh_token -msgid "Refresh Token" -msgstr "" - -#. module: odex30_account_online_sync -#. odoo-python -#: code:addons/odex30_account_online_sync/models/account_journal.py:0 -msgid "Report Issue" -msgstr "" - -#. module: odex30_account_online_sync -#. odoo-python -#: code:addons/odex30_account_online_sync/models/account_online.py:0 -msgid "Report issue" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,help:account_online_synchronization.field_account_online_link__client_id -msgid "Represent a link for a given user towards a banking institution" -msgstr "" - -#. module: odex30_account_online_sync -#: model_terms:ir.ui.view,arch_db:account_online_synchronization.account_online_link_view_form -msgid "Reset" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_online_link__activity_user_id -msgid "Responsible User" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_online_link__message_has_sms_error -msgid "SMS Delivery error" -msgstr "" - -#. module: odex30_account_online_sync -#. odoo-javascript -#: code:addons/odex30_account_online_sync/static/src/components/bank_configure/bank_configure.xml:0 -msgid "Search over" -msgstr "" - -#. module: odex30_account_online_sync -#: model_terms:ir.ui.view,arch_db:account_online_synchronization.portal_renew_consent -msgid "" -"Security Tip: always check the domain name of this page, before clicking on " -"the button." -msgstr "" - -#. module: odex30_account_online_sync -#. odoo-javascript -#: code:addons/odex30_account_online_sync/static/src/components/refresh_spin_journal_widget/refresh_spin_journal_widget.xml:0 -msgid "See error" -msgstr "" - -#. module: odex30_account_online_sync -#. odoo-python -#: code:addons/odex30_account_online_sync/models/account_online.py:0 -#: model_terms:ir.ui.view,arch_db:account_online_synchronization.view_account_bank_selection_form_wizard -msgid "Select a Bank Account" -msgstr "" - -#. module: odex30_account_online_sync -#: model_terms:ir.ui.view,arch_db:account_online_synchronization.view_account_bank_selection_form_wizard -msgid "Select the" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_bank_selection__selected_account -msgid "Selected Account" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_bank_statement_line_transient__sequence -msgid "Sequence" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,help:account_online_synchronization.field_account_online_account__account_number -msgid "Set if third party provider has the full account number" -msgstr "" - -#. module: odex30_account_online_sync -#. odoo-javascript -#: code:addons/odex30_account_online_sync/static/src/components/bank_configure/bank_configure.xml:0 -msgid "Setup Bank" -msgstr "" - -#. module: odex30_account_online_sync -#. odoo-python -#: code:addons/odex30_account_online_sync/models/account_online.py:0 -msgid "Setup Bank Account" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_online_link__show_sync_actions -msgid "Show Sync Actions" -msgstr "" - -#. module: odex30_account_online_sync -#. odoo-javascript -#: code:addons/odex30_account_online_sync/static/src/components/views/account_online_authorization_kanban_controller.xml:0 -msgid "Some transactions" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_duplicate_transaction_wizard__date -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_missing_transaction_wizard__date -msgid "Starting Date" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_bank_statement_line_transient__state -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_journal__account_online_link_state -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_online_link__state -msgid "State" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,help:account_online_synchronization.field_account_online_link__activity_state -msgid "" -"Status based on activities\n" -"Overdue: Due date is already passed\n" -"Today: Activity date is today\n" -"Planned: Future activities." -msgstr "" - -#. module: odex30_account_online_sync -#: model_terms:ir.ui.view,arch_db:account_online_synchronization.portal_renew_consent -msgid "Thank You!" -msgstr "" - -#. module: odex30_account_online_sync -#. odoo-python -#: code:addons/odex30_account_online_sync/models/account_online.py:0 -msgid "The consent for the selected account has expired." -msgstr "" - -#. module: odex30_account_online_sync -#. odoo-python -#: code:addons/odex30_account_online_sync/models/account_online.py:0 -msgid "" -"The online synchronization service is not available at the moment. Please " -"try again later." -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,help:account_online_synchronization.field_account_online_link__provider_type -msgid "Third Party Provider" -msgstr "" - -#. module: odex30_account_online_sync -#: model_terms:ir.ui.view,arch_db:account_online_synchronization.account_duplicate_transaction_wizard_view_form -msgid "" -"This action will delete all selected transactions. Are you sure you want to " -"proceed?" -msgstr "" - -#. module: odex30_account_online_sync -#: model_terms:ir.ui.view,arch_db:account_online_synchronization.account_online_link_view_form -msgid "This button will reset the fetching status" -msgstr "" - -#. module: odex30_account_online_sync -#. odoo-python -#: code:addons/odex30_account_online_sync/models/account_online.py:0 -msgid "" -"This version of Odoo appears to be outdated and does not support the '%s' " -"sync mode. Installing the latest update might solve this." -msgstr "" - -#. module: odex30_account_online_sync -#: model_terms:ir.actions.act_window,help:account_online_synchronization.action_account_online_link_form -msgid "" -"To create a synchronization with your banking institution,
\n" -" please click on Add a Bank Account." -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,help:account_online_synchronization.field_account_online_link__access_token -msgid "Token used to access API." -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,help:account_online_synchronization.field_account_online_link__refresh_token -msgid "Token used to sign API request, Never disclose it" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_duplicate_transaction_wizard__transaction_ids -msgid "Transaction" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_bank_statement_line_transient__transaction_details -msgid "Transaction Details" -msgstr "" - -#. module: odex30_account_online_sync -#: model_terms:ir.ui.view,arch_db:account_online_synchronization.account_duplicate_transaction_wizard_view_form -msgid "Transactions" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model,name:account_online_synchronization.model_account_bank_statement_line_transient -msgid "Transient model for bank statement line" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,help:account_online_synchronization.field_account_online_link__has_unlinked_accounts -msgid "" -"True if that connection still has accounts that are not linked to an Odoo " -"journal" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,help:account_online_synchronization.field_account_online_link__activity_exception_decoration -msgid "Type of the exception activity on record." -msgstr "" - -#. module: odex30_account_online_sync -#: model_terms:ir.ui.view,arch_db:account_online_synchronization.account_online_link_view_form -msgid "Update Credentials" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields.selection,name:account_online_synchronization.selection__account_online_account__fetching_status__waiting -msgid "Waiting" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:account_online_synchronization.field_account_online_link__website_message_ids -msgid "Website Messages" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,help:account_online_synchronization.field_account_online_link__website_message_ids -msgid "Website communication history" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model,name:account_online_synchronization.model_account_duplicate_transaction_wizard -msgid "Wizard for duplicate transactions" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model,name:account_online_synchronization.model_account_missing_transaction_wizard -msgid "Wizard for missing transactions" -msgstr "" - -#. module: odex30_account_online_sync -#. odoo-javascript -#: code:addons/odex30_account_online_sync/static/src/components/transient_bank_statement_line_list_view/transient_bank_statement_line_list_view.xml:0 -msgid "" -"You are importing transactions before the creation of your online synchronization\n" -" (" -msgstr "" - -#. module: odex30_account_online_sync -#. odoo-python -#: code:addons/odex30_account_online_sync/models/account_online.py:0 -msgid "You can contact Odoo support" -msgstr "" - -#. module: odex30_account_online_sync -#. odoo-python -#: code:addons/odex30_account_online_sync/models/account_journal.py:0 -msgid "You can only execute this action for bank-synchronized journals." -msgstr "" - -#. module: odex30_account_online_sync -#. odoo-python -#: code:addons/odex30_account_online_sync/models/account_journal.py:0 -#: code:addons/odex30_account_online_sync/wizard/account_journal_missing_transactions.py:0 -msgid "" -"You can't find missing transactions for a journal that isn't connected." -msgstr "" - -#. module: odex30_account_online_sync -#. odoo-python -#: code:addons/odex30_account_online_sync/models/account_journal.py:0 -#: code:addons/odex30_account_online_sync/models/account_online.py:0 -msgid "You cannot have two journals associated with the same Online Account." -msgstr "" - -#. module: odex30_account_online_sync -#. odoo-python -#: code:addons/odex30_account_online_sync/wizard/account_bank_statement_line.py:0 -msgid "You cannot import pending transactions." -msgstr "" - -#. module: odex30_account_online_sync -#. odoo-javascript -#: code:addons/odex30_account_online_sync/static/src/components/transient_bank_statement_line_list_view/transient_bank_statement_line_list_view.xml:0 -msgid "You have" -msgstr "" - -#. module: odex30_account_online_sync -#. odoo-python -#: code:addons/odex30_account_online_sync/wizard/account_journal_missing_transactions.py:0 -msgid "You have to select one journal to continue." -msgstr "" - -#. module: odex30_account_online_sync -#: model:mail.template,subject:account_online_synchronization.email_template_sync_reminder -msgid "Your bank connection is expiring soon" -msgstr "" - -#. module: odex30_account_online_sync -#: model_terms:ir.ui.view,arch_db:account_online_synchronization.view_account_bank_selection_form_wizard -msgid "account to connect:" -msgstr "" - -#. module: odex30_account_online_sync -#. odoo-javascript -#: code:addons/odex30_account_online_sync/static/src/components/bank_configure/bank_configure.xml:0 -msgid "banks" -msgstr "" - -#. module: odex30_account_online_sync -#. odoo-javascript -#: code:addons/odex30_account_online_sync/static/src/components/transient_bank_statement_line_list_view/transient_bank_statement_line_list_view.xml:0 -msgid "entries" -msgstr "" - -#. module: odex30_account_online_sync -#. odoo-javascript -#: code:addons/odex30_account_online_sync/static/src/components/bank_configure/bank_configure.xml:0 -msgid "loading..." -msgstr "" - -#. module: odex30_account_online_sync -#. odoo-javascript -#: code:addons/odex30_account_online_sync/static/src/components/views/account_online_authorization_kanban_controller.xml:0 -msgid "may be duplicates." -msgstr "" - -#. module: odex30_account_online_sync -#: model_terms:ir.ui.view,arch_db:account_online_synchronization.portal_renew_consent -msgid "on" -msgstr "" - -#. module: odex30_account_online_sync -#: model:ir.model,name:account_online_synchronization.model_account_online_account -msgid "representation of an online bank account" -msgstr "" - -#. module: odex30_account_online_sync -#. odoo-javascript -#: code:addons/odex30_account_online_sync/static/src/components/refresh_spin_journal_widget/refresh_spin_journal_widget.xml:0 -msgid "transactions fetched" -msgstr "" - -#. module: odex30_account_online_sync -#. odoo-javascript -#: code:addons/odex30_account_online_sync/static/src/components/transient_bank_statement_line_list_view/transient_bank_statement_line_list_view.xml:0 -msgid "" -"within this period that were not created using the online synchronization. " -"This might cause duplicate entries." -msgstr "" diff --git a/dev_odex30_accounting/odex30_account_online_sync/i18n/ar.po b/dev_odex30_accounting/odex30_account_online_sync/i18n/ar.po deleted file mode 100644 index 61d8a10..0000000 --- a/dev_odex30_accounting/odex30_account_online_sync/i18n/ar.po +++ /dev/null @@ -1,1418 +0,0 @@ -# Translation of Odoo Server. -# This file contains the translation of the following modules: -# * odex30_account_online_sync -# -# Translators: -# Mustafa J. Kadhem , 2024 -# Malaz Abuidris , 2025 -# Wil Odoo, 2025 -# -msgid "" -msgstr "" -"Project-Id-Version: Odoo Server 18.0+e\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-07-11 18:50+0000\n" -"PO-Revision-Date: 2024-09-25 09:44+0000\n" -"Last-Translator: Wil Odoo, 2025\n" -"Language-Team: Arabic (https://app.transifex.com/odoo/teams/41243/ar/)\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: \n" -"Language: ar\n" -"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n" - -#. module: odex30_account_online_sync -#. odoo-python -#: code:addons/odex30_account_online_sync/models/account_online.py:0 -msgid "" -"\n" -"\n" -"If you've already opened a ticket for this issue, don't report it again: a support agent will contact you shortly." -msgstr "" -"\n" -"\n" -"إذا قمت بفتح تذكرة دعم لهذه المشكلة بالفعل، لا تقم بالإبلاغ عنها مجدداً: سيتواصل معك أحد موظفي الدعم عما قريب. " - -#. module: odex30_account_online_sync -#. odoo-python -#: code:addons/odex30_account_online_sync/wizard/account_journal_duplicate_transactions.py:0 -msgid "%s duplicate transactions" -msgstr "%s معاملات مكررة " - -#. module: odex30_account_online_sync -#. odoo-javascript -#: code:addons/odex30_account_online_sync/static/src/components/transient_bank_statement_line_list_view/transient_bank_statement_line_list_view.xml:0 -msgid "" -").\n" -" This might cause duplicate entries." -msgstr "" -").\n" -" قد يتسبب هذا في تكرار القيود. " - -#. module: odex30_account_online_sync -#. odoo-javascript -#: code:addons/odex30_account_online_sync/static/src/components/refresh_spin_journal_widget/refresh_spin_journal_widget.xml:0 -msgid "0 transaction fetched" -msgstr "تم جلب 0 معاملات " - -#. module: odex30_account_online_sync -#: model_terms:ir.ui.view,arch_db:odex30_account_online_sync.account_duplicate_transaction_wizard_view_form -msgid " Delete Selected" -msgstr " حذف العناصر المحددة " - -#. module: odex30_account_online_sync -#: model_terms:ir.ui.view,arch_db:odex30_account_online_sync.account_journal_form -msgid " Send Now" -msgstr " إرسال الآن " - -#. module: odex30_account_online_sync -#: model:mail.template,body_html:odex30_account_online_sync.email_template_sync_reminder -msgid "" -"\n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -"
\n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -"
\n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -"
\n" -"
\n" -" Hello,

\n" -" The connection between https://yourcompany.odoo.com and Belfius expired.expires in 10 days.
\n" -" \n" -" Security Tip: Check that the domain name you are redirected to is: https://yourcompany.odoo.com\n" -"
\n" -"
\n" -"
\n" -"
\n" -"
\n" -"
\n" -" \n" -" \n" -" \n" -" \n" -"
\n" -" Powered by Odoo\n" -"
\n" -" \n" -" \n" -" \n" -" \n" -"
\n" -" PS: This is an automated email sent by Odoo Accounting to remind you before a bank sync consent expiration.\n" -"
\n" -"
\n" -" " -msgstr "" -"\n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -"
\n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -"
\n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -" \n" -"
\n" -"
\n" -" مرحباً،

\n" -" الاتصال بين https://yourcompany.odoo.com و Belfius قد انتهت صلاحيته.تنتهي صلاحيته في 10 أيام.
\n" -" \n" -" نصيحة للأمان: تحقق من أن اسم النطاق الذي تمت إعادة توجيهك إليه هو: https://yourcompany.odoo.com\n" -"
\n" -"
\n" -"
\n" -"
\n" -"
\n" -"
\n" -" \n" -" \n" -" \n" -" \n" -"
\n" -" مشغل بواسطة أودو\n" -"
\n" -" \n" -" \n" -" \n" -" \n" -"
\n" -" ملاحظة: هذا البريد الإلكتروني التلقائي مرسل بواسطة محاسبة أودو لتذكيرك قبل انتهاء صلاحية إذن مزامنة البنك.\n" -"
\n" -"
\n" -" " - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_online_link__access_token -msgid "Access Token" -msgstr "رمز الوصول " - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_online_account__account_data -msgid "Account Data" -msgstr "بيانات الحساب " - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_online_account__name -msgid "Account Name" -msgstr "اسم الحساب" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,help:odex30_account_online_sync.field_account_online_account__name -msgid "Account Name as provided by third party provider" -msgstr "اسم الحساب حسب مزود الطرف الثالث " - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_bank_statement_line_transient__account_number -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_online_account__account_number -msgid "Account Number" -msgstr "رقم الحساب" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_bank_selection__account_online_account_ids -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_bank_statement_line_transient__online_account_id -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_journal__account_online_account_id -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_online_link__account_online_account_ids -msgid "Account Online Account" -msgstr "حساب عبر الإنترنت " - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_bank_selection__account_online_link_id -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_bank_statement_line__online_link_id -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_journal__account_online_link_id -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_online_account__account_online_link_id -msgid "Account Online Link" -msgstr "ربط الحساب عبر الإنترنت " - -#. module: odex30_account_online_sync -#: model:ir.actions.server,name:odex30_account_online_sync.online_sync_cron_waiting_synchronization_ir_actions_server -msgid "Account: Journal online Waiting Synchronization" -msgstr "الحساب: دفتر اليومية أونلاين بانتظار المزامنة " - -#. module: odex30_account_online_sync -#: model:ir.actions.server,name:odex30_account_online_sync.online_sync_cron_ir_actions_server -msgid "Account: Journal online sync" -msgstr "الحساب: مزامنة دفتر اليومية عبر الإنترنت " - -#. module: odex30_account_online_sync -#: model:ir.actions.server,name:odex30_account_online_sync.online_sync_unused_connection_cron_ir_actions_server -msgid "Account: Journal online sync cleanup unused connections" -msgstr "الحساب: مسح الاتصالات غير المستخدمة عند مزامنة دفتر اليومية أونلاين " - -#. module: odex30_account_online_sync -#: model:ir.actions.server,name:odex30_account_online_sync.online_sync_mail_cron_ir_actions_server -msgid "Account: Journal online sync reminder" -msgstr "الحساب: تذكير مزامنة دفتر اليومية أونلاين " - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_online_link__message_needaction -msgid "Action Needed" -msgstr "إجراء مطلوب" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_online_link__activity_ids -msgid "Activities" -msgstr "الأنشطة" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_online_link__activity_exception_decoration -msgid "Activity Exception Decoration" -msgstr "زخرفة استثناء النشاط" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_online_link__activity_state -msgid "Activity State" -msgstr "حالة النشاط" - -#. module: odex30_account_online_sync -#: model:ir.model,name:odex30_account_online_sync.model_mail_activity_type -msgid "Activity Type" -msgstr "نوع النشاط" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_online_link__activity_type_icon -msgid "Activity Type Icon" -msgstr "أيقونة نوع النشاط" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_bank_statement_line_transient__amount -msgid "Amount" -msgstr "مبلغ" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_bank_statement_line_transient__amount_currency -msgid "Amount in Currency" -msgstr "المبلغ بالعملة" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_online_link__message_attachment_count -msgid "Attachment Count" -msgstr "عدد المرفقات" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_online_link__auto_sync -msgid "Automatic synchronization" -msgstr "المزامنة الآلية " - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_online_account__balance -msgid "Balance" -msgstr "الرصيد" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,help:odex30_account_online_sync.field_account_online_account__balance -msgid "Balance of the account sent by the third party provider" -msgstr "رصيد الحساب الذي أرسله مزود الطرف الثالث " - -#. module: odex30_account_online_sync -#: model_terms:ir.ui.view,arch_db:odex30_account_online_sync.portal_renew_consent -msgid "Bank" -msgstr "البنك" - -#. module: odex30_account_online_sync -#: model:ir.model,name:odex30_account_online_sync.model_account_online_link -msgid "Bank Connection" -msgstr "اتصال البنك" - -#. module: odex30_account_online_sync -#: model:ir.model,name:odex30_account_online_sync.model_account_bank_statement_line -msgid "Bank Statement Line" -msgstr "بند كشف الحساب البنكي" - -#. module: odex30_account_online_sync -#: model:mail.activity.type,name:odex30_account_online_sync.bank_sync_activity_update_consent -msgid "Bank Synchronization: Update consent" -msgstr "مزامنة البنك: تحديث الإذن " - -#. module: odex30_account_online_sync -#. odoo-python -#: code:addons/odex30_account_online_sync/models/account_online.py:0 -msgid "Bank Synchronization: Update your consent" -msgstr "مزامنة البنك: قم بتحديث الإذن " - -#. module: odex30_account_online_sync -#: model:mail.template,name:odex30_account_online_sync.email_template_sync_reminder -msgid "Bank connection expiration reminder" -msgstr "تذكير انتهاء صلاحية اتصال البنك " - -#. module: odex30_account_online_sync -#: model:ir.model,name:odex30_account_online_sync.model_bank_rec_widget -msgid "Bank reconciliation widget for a single statement line" -msgstr "أداة التسوية البنكية لبند كشف حساب واحد " - -#. module: odex30_account_online_sync -#: model_terms:ir.ui.view,arch_db:odex30_account_online_sync.account_missing_transaction_wizard_view_form -#: model_terms:ir.ui.view,arch_db:odex30_account_online_sync.view_account_bank_selection_form_wizard -msgid "Cancel" -msgstr "إلغاء" - -#. module: odex30_account_online_sync -#. odoo-python -#: code:addons/odex30_account_online_sync/models/account_online.py:0 -msgid "Check the documentation" -msgstr "ألقِ نظرة على الوثائق " - -#. module: odex30_account_online_sync -#: model_terms:ir.ui.view,arch_db:odex30_account_online_sync.account_duplicate_transaction_wizard_view_form -msgid "" -"Choose a date and a journal from which you want to check the transactions." -msgstr "اختر التاريخ ودفتر اليومية الذي ترغب في التحقق من المعاملات فيه " - -#. module: odex30_account_online_sync -#: model_terms:ir.ui.view,arch_db:odex30_account_online_sync.account_missing_transaction_wizard_view_form -msgid "Choose a date and a journal from which you want to fetch transactions" -msgstr "اختر التاريخ ودفتر اليومية الذي ترغب في جلب المعاملات منه " - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_online_link__client_id -msgid "Client" -msgstr "العميل" - -#. module: odex30_account_online_sync -#: model_terms:ir.ui.view,arch_db:odex30_account_online_sync.account_online_link_view_form -msgid "Client id" -msgstr "معرف العميل " - -#. module: odex30_account_online_sync -#: model:ir.model.fields,help:odex30_account_online_sync.field_account_journal__renewal_contact_email -msgid "" -"Comma separated list of email addresses to send consent renewal " -"notifications 15, 3 and 1 days before expiry" -msgstr "" -"قائمة مفصولة بفواصل بعناوين البريد الإلكترونية لإرسال إشعارات تجديد الإذن " -"قبل 15، 3، و1 يوم من تاريخ انتهاء الصلاحية " - -#. module: odex30_account_online_sync -#: model:ir.model,name:odex30_account_online_sync.model_res_company -msgid "Companies" -msgstr "الشركات" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_bank_statement_line_transient__company_id -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_online_account__company_id -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_online_link__company_id -msgid "Company" -msgstr "الشركة " - -#. module: odex30_account_online_sync -#: model_terms:ir.ui.view,arch_db:odex30_account_online_sync.account_online_link_view_form -msgid "Connect" -msgstr "اتصل" - -#. module: odex30_account_online_sync -#: model_terms:ir.ui.view,arch_db:odex30_account_online_sync.view_account_bank_selection_form_wizard -msgid "Connect Bank" -msgstr "توصيل البنك " - -#. module: odex30_account_online_sync -#: model_terms:ir.ui.view,arch_db:odex30_account_online_sync.account_journal_dashboard_inherit_online_sync -msgid "Connect bank" -msgstr "توصيل البنك " - -#. module: odex30_account_online_sync -#: model_terms:ir.ui.view,arch_db:odex30_account_online_sync.portal_renew_consent -msgid "Connect my Bank" -msgstr "توصيل بنكي " - -#. module: odex30_account_online_sync -#: model_terms:ir.ui.view,arch_db:odex30_account_online_sync.portal_renew_consent -msgid "Connect your bank account to Odoo" -msgstr "قم بتوصيل حسابك البنكي بأودو " - -#. module: odex30_account_online_sync -#: model:ir.model.fields.selection,name:odex30_account_online_sync.selection__account_online_link__state__connected -msgid "Connected" -msgstr "متصل " - -#. module: odex30_account_online_sync -#. odoo-javascript -#: code:addons/odex30_account_online_sync/static/src/components/connected_until_widget/connected_until_widget.xml:0 -msgid "Connected until" -msgstr "متصل حتى " - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_journal__renewal_contact_email -msgid "Connection Requests" -msgstr "طلبات الاتصال " - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_online_link__connection_state_details -msgid "Connection State Details" -msgstr "تفاصيل حالة الاتصال " - -#. module: odex30_account_online_sync -#: model:mail.message.subtype,name:odex30_account_online_sync.bank_sync_consent_renewal -msgid "Consent Renewal" -msgstr "تجديد الإذن " - -#. module: odex30_account_online_sync -#: model:ir.model,name:odex30_account_online_sync.model_res_partner -msgid "Contact" -msgstr "جهة الاتصال" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_bank_selection__create_uid -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_bank_statement_line_transient__create_uid -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_duplicate_transaction_wizard__create_uid -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_missing_transaction_wizard__create_uid -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_online_account__create_uid -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_online_link__create_uid -msgid "Created by" -msgstr "أنشئ بواسطة" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_bank_selection__create_date -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_bank_statement_line_transient__create_date -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_duplicate_transaction_wizard__create_date -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_missing_transaction_wizard__create_date -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_online_account__create_date -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_online_link__create_date -msgid "Created on" -msgstr "أنشئ في" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_bank_statement_line_transient__currency_id -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_online_account__currency_id -msgid "Currency" -msgstr "العملة" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_bank_statement_line_transient__date -msgid "Date" -msgstr "التاريخ" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,help:odex30_account_online_sync.field_account_journal__expiring_synchronization_date -#: model:ir.model.fields,help:odex30_account_online_sync.field_account_online_link__expiring_synchronization_date -msgid "Date when the consent for this connection expires" -msgstr "تاريخ انتهاء صلاحية إذن هذا الاتصال " - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_bank_selection__display_name -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_bank_statement_line_transient__display_name -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_duplicate_transaction_wizard__display_name -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_missing_transaction_wizard__display_name -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_online_account__display_name -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_online_link__display_name -msgid "Display Name" -msgstr "اسم العرض " - -#. module: odex30_account_online_sync -#: model:ir.model.fields.selection,name:odex30_account_online_sync.selection__account_online_account__fetching_status__done -msgid "Done" -msgstr "منتهي " - -#. module: odex30_account_online_sync -#. odoo-python -#: code:addons/odex30_account_online_sync/models/account_bank_statement.py:0 -#: model:ir.model.fields.selection,name:odex30_account_online_sync.selection__account_online_link__state__error -msgid "Error" -msgstr "خطأ" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_journal__expiring_synchronization_date -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_online_link__expiring_synchronization_date -msgid "Expiring Synchronization Date" -msgstr "تاريخ انتهاء صلاحية المزامنة " - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_journal__expiring_synchronization_due_day -msgid "Expiring Synchronization Due Day" -msgstr "التاريخ الذي ستنتهي فيه صلاحية المزامنة " - -#. module: odex30_account_online_sync -#. odoo-javascript -#: code:addons/odex30_account_online_sync/static/src/components/connected_until_widget/connected_until_widget.xml:0 -msgid "Extend Connection" -msgstr "تمديد الإذن " - -#. module: odex30_account_online_sync -#: model:ir.model.fields,help:odex30_account_online_sync.field_account_online_account__account_data -msgid "Extra information needed by third party provider" -msgstr "معلومات إضافية مطلوبة من قِبَل مزود الطرف الثالث " - -#. module: odex30_account_online_sync -#: model_terms:ir.ui.view,arch_db:odex30_account_online_sync.account_missing_transaction_wizard_view_form -msgid "Fetch" -msgstr "جلب " - -#. module: odex30_account_online_sync -#: model_terms:ir.ui.view,arch_db:odex30_account_online_sync.account_duplicate_transaction_wizard_view_form -#: model_terms:ir.ui.view,arch_db:odex30_account_online_sync.account_missing_transaction_wizard_view_form -msgid "Fetch Missing Bank Statements Wizard" -msgstr "معالج جلب كشوفات الحسابات المفقودة " - -#. module: odex30_account_online_sync -#. odoo-javascript -#: code:addons/odex30_account_online_sync/static/src/components/refresh_spin_journal_widget/refresh_spin_journal_widget.xml:0 -#: model_terms:ir.ui.view,arch_db:odex30_account_online_sync.account_online_link_view_form -msgid "Fetch Transactions" -msgstr "جلب المعاملات " - -#. module: odex30_account_online_sync -#. odoo-python -#: code:addons/odex30_account_online_sync/models/account_online.py:0 -msgid "Fetched Transactions" -msgstr "المعاملات التي تم جلبها " - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_journal__online_sync_fetching_status -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_online_account__fetching_status -msgid "Fetching Status" -msgstr "حالة الجلب " - -#. module: odex30_account_online_sync -#. odoo-javascript -#: code:addons/odex30_account_online_sync/static/src/components/refresh_spin_journal_widget/refresh_spin_journal_widget.xml:0 -msgid "Fetching..." -msgstr "جري جلب... " - -#. module: odex30_account_online_sync -#. odoo-javascript -#. odoo-python -#: code:addons/odex30_account_online_sync/models/account_journal.py:0 -#: code:addons/odex30_account_online_sync/static/src/components/bank_reconciliation/find_duplicate_transactions_cog_menu.xml:0 -msgid "Find Duplicate Transactions" -msgstr "العثور على المعاملات المكررة " - -#. module: odex30_account_online_sync -#. odoo-javascript -#. odoo-python -#: code:addons/odex30_account_online_sync/models/account_journal.py:0 -#: code:addons/odex30_account_online_sync/static/src/components/bank_reconciliation/fetch_missing_transactions_cog_menu.xml:0 -msgid "Find Missing Transactions" -msgstr "العثور على المعاملات المفقودة " - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_duplicate_transaction_wizard__first_ids_in_group -msgid "First Ids In Group" -msgstr "المُعرّفات الأولى في المجموعة " - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_online_link__message_follower_ids -msgid "Followers" -msgstr "المتابعين" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_online_link__message_partner_ids -msgid "Followers (Partners)" -msgstr "المتابعين (الشركاء) " - -#. module: odex30_account_online_sync -#: model:ir.model.fields,help:odex30_account_online_sync.field_account_online_link__activity_type_icon -msgid "Font awesome icon e.g. fa-tasks" -msgstr "أيقونة من Font awesome مثال: fa-tasks " - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_bank_statement_line_transient__foreign_currency_id -msgid "Foreign Currency" -msgstr "عملة أجنبية " - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_online_link__has_message -msgid "Has Message" -msgstr "يحتوي على رسالة " - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_online_link__has_unlinked_accounts -msgid "Has Unlinked Accounts" -msgstr "يحتوي على حسابات غير مرتبطة " - -#. module: odex30_account_online_sync -#. odoo-python -#: code:addons/odex30_account_online_sync/models/account_online.py:0 -msgid "Here" -msgstr "هنا " - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_bank_selection__id -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_bank_statement_line_transient__id -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_duplicate_transaction_wizard__id -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_missing_transaction_wizard__id -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_online_account__id -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_online_link__id -msgid "ID" -msgstr "المُعرف" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_online_link__activity_exception_icon -msgid "Icon" -msgstr "الأيقونة" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,help:odex30_account_online_sync.field_account_online_link__activity_exception_icon -msgid "Icon to indicate an exception activity." -msgstr "الأيقونة للإشارة إلى النشاط المستثنى. " - -#. module: odex30_account_online_sync -#: model:ir.model.fields,help:odex30_account_online_sync.field_account_online_account__online_identifier -msgid "Id used to identify account by third party provider" -msgstr "المعرف المستخدَم لتعرف الحساب من قِبَل مزود الطرف الثالث " - -#. module: odex30_account_online_sync -#: model:ir.model.fields,help:odex30_account_online_sync.field_account_online_link__message_needaction -msgid "If checked, new messages require your attention." -msgstr "إذا كان محددًا، فهناك رسائل جديدة عليك رؤيتها. " - -#. module: odex30_account_online_sync -#: model:ir.model.fields,help:odex30_account_online_sync.field_account_online_link__message_has_error -#: model:ir.model.fields,help:odex30_account_online_sync.field_account_online_link__message_has_sms_error -msgid "If checked, some messages have a delivery error." -msgstr "إذا كان محددًا، فقد حدث خطأ في تسليم بعض الرسائل." - -#. module: odex30_account_online_sync -#: model:ir.model.fields,help:odex30_account_online_sync.field_account_online_account__inverse_balance_sign -msgid "If checked, the balance sign will be inverted" -msgstr "إذا كان محدداً، سيتم عكس علامة الرصيد " - -#. module: odex30_account_online_sync -#: model:ir.model.fields,help:odex30_account_online_sync.field_account_online_account__inverse_transaction_sign -msgid "If checked, the transaction sign will be inverted" -msgstr "إذا كان محدداً، سيتم عكس علامة المعاملة " - -#. module: odex30_account_online_sync -#: model:ir.model.fields,help:odex30_account_online_sync.field_account_online_link__auto_sync -msgid "" -"If possible, we will try to automatically fetch new transactions for this record\n" -" \n" -"If the automatic sync is disabled. that will be due to security policy on the bank's end. So, they have to launch the sync manually" -msgstr "" -"إذا أمكن، سنحاول جلب المعاملات الجديدة تلقائياً لهذا السجل\n" -" \n" -"إذا كانت خاصية المزامنة التلقائية معطلة. سيكون ذلك بسبب سياسة الأمن التي يفرضها البنك. لذلك، سيتوجب عليهم تشغيل عملية المزامنة يدوياً " - -#. module: odex30_account_online_sync -#. odoo-javascript -#: code:addons/odex30_account_online_sync/static/src/components/transient_bank_statement_line_list_view/transient_bank_statement_line_list_view.xml:0 -msgid "Import Transactions" -msgstr "استيراد المعاملات " - -#. module: odex30_account_online_sync -#: model:ir.model.fields,help:odex30_account_online_sync.field_account_online_link__provider_data -msgid "Information needed to interact with third party provider" -msgstr "المعلومات المطلوبة للتفاعل مع مزود الطرف الثالث " - -#. module: odex30_account_online_sync -#: model:ir.model.fields,help:odex30_account_online_sync.field_account_bank_selection__institution_name -#: model:ir.model.fields,help:odex30_account_online_sync.field_account_online_link__name -msgid "Institution Name" -msgstr "اسم المنشأة " - -#. module: odex30_account_online_sync -#. odoo-python -#: code:addons/odex30_account_online_sync/models/account_online.py:0 -msgid "Internal Error" -msgstr "خطأ داخلي " - -#. module: odex30_account_online_sync -#. odoo-python -#: code:addons/odex30_account_online_sync/models/account_online.py:0 -msgid "Invalid URL" -msgstr "رابط URL غير صحيح " - -#. module: odex30_account_online_sync -#. odoo-python -#: code:addons/odex30_account_online_sync/models/account_online.py:0 -msgid "Invalid value for proxy_mode config parameter." -msgstr "قيمة غير صالحة لمعيار تهيئة proxy_mode " - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_online_account__inverse_balance_sign -msgid "Inverse Balance Sign" -msgstr "علامة عكس الرصيد " - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_online_account__inverse_transaction_sign -msgid "Inverse Transaction Sign" -msgstr "عكس علامة المعاملة " - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_online_link__message_is_follower -msgid "Is Follower" -msgstr "متابع" - -#. module: odex30_account_online_sync -#: model:ir.model,name:odex30_account_online_sync.model_account_journal -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_bank_statement_line_transient__journal_id -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_duplicate_transaction_wizard__journal_id -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_missing_transaction_wizard__journal_id -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_online_account__journal_ids -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_online_link__journal_ids -#: model_terms:ir.ui.view,arch_db:odex30_account_online_sync.portal_renew_consent -msgid "Journal" -msgstr "دفتر اليومية" - -#. module: odex30_account_online_sync -#. odoo-python -#: code:addons/odex30_account_online_sync/models/account_online.py:0 -msgid "" -"Journal %(journal_name)s has been set up with a different currency and " -"already has existing entries. You can't link selected bank account in " -"%(currency_name)s to it" -msgstr "" -"تم إعداد دفتر اليومية %(journal_name)s بعملة مختلفة ويحتوي بالفعل على قيود " -"موجودة. لا يمكنك ربط الحساب البنكي المحدد %(currency_name)s به " - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_online_link__last_refresh -msgid "Last Refresh" -msgstr "التحديث الأخير" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_bank_selection__write_uid -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_bank_statement_line_transient__write_uid -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_duplicate_transaction_wizard__write_uid -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_missing_transaction_wizard__write_uid -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_online_account__write_uid -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_online_link__write_uid -msgid "Last Updated by" -msgstr "آخر تحديث بواسطة" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_bank_selection__write_date -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_bank_statement_line_transient__write_date -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_duplicate_transaction_wizard__write_date -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_missing_transaction_wizard__write_date -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_online_account__write_date -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_online_link__write_date -msgid "Last Updated on" -msgstr "آخر تحديث في" - -#. module: odex30_account_online_sync -#: model_terms:ir.ui.view,arch_db:odex30_account_online_sync.account_online_link_view_form -msgid "Last refresh" -msgstr "آخر تحديث " - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_online_account__last_sync -msgid "Last synchronization" -msgstr "آخر مزامنة" - -#. module: odex30_account_online_sync -#: model_terms:ir.ui.view,arch_db:odex30_account_online_sync.portal_renew_consent -msgid "Latest Balance" -msgstr "آخر رصيد " - -#. module: odex30_account_online_sync -#: model:ir.model,name:odex30_account_online_sync.model_account_bank_selection -msgid "Link a bank account to the selected journal" -msgstr " قم بربط حساب بنكي واحد بدفتر اليومية المحدد " - -#. module: odex30_account_online_sync -#. odoo-python -#: code:addons/odex30_account_online_sync/wizard/account_journal_missing_transactions.py:0 -msgid "Manual Bank Statement Lines" -msgstr "بنود كشف الحساب البنكي اليدوية " - -#. module: odex30_account_online_sync -#. odoo-python -#: code:addons/odex30_account_online_sync/models/account_online.py:0 -msgid "Message" -msgstr "الرسالة" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_online_link__message_has_error -msgid "Message Delivery error" -msgstr "خطأ في تسليم الرسائل" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_online_link__message_ids -msgid "Messages" -msgstr "الرسائل" - -#. module: odex30_account_online_sync -#. odoo-python -#: code:addons/odex30_account_online_sync/wizard/account_journal_missing_transactions.py:0 -msgid "Missing and Pending Transactions" -msgstr "المعاملات المفقودة وقيد الانتظار " - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_online_link__my_activity_date_deadline -msgid "My Activity Deadline" -msgstr "الموعد النهائي لنشاطاتي " - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_bank_selection__institution_name -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_online_link__name -msgid "Name" -msgstr "الاسم" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_online_link__activity_calendar_event_id -msgid "Next Activity Calendar Event" -msgstr "الفعالية التالية في تقويم الأنشطة " - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_online_link__activity_date_deadline -msgid "Next Activity Deadline" -msgstr "الموعد النهائي للنشاط التالي" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_online_link__activity_summary -msgid "Next Activity Summary" -msgstr "ملخص النشاط التالي" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_online_link__activity_type_id -msgid "Next Activity Type" -msgstr "نوع النشاط التالي" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_online_link__next_refresh -msgid "Next synchronization" -msgstr "المزامنة التالية" - -#. module: odex30_account_online_sync -#: model:ir.model.fields.selection,name:odex30_account_online_sync.selection__account_online_link__state__disconnected -msgid "Not Connected" -msgstr "غير متصل " - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_online_link__message_needaction_counter -msgid "Number of Actions" -msgstr "عدد الإجراءات" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_online_link__message_has_error_counter -msgid "Number of errors" -msgstr "عدد الأخطاء " - -#. module: odex30_account_online_sync -#: model:ir.model.fields,help:odex30_account_online_sync.field_account_online_link__message_needaction_counter -msgid "Number of messages requiring action" -msgstr "عدد الرسائل التي تتطلب اتخاذ إجراء" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,help:odex30_account_online_sync.field_account_online_link__message_has_error_counter -msgid "Number of messages with delivery error" -msgstr "عدد الرسائل الحادث بها خطأ في التسليم" - -#. module: odex30_account_online_sync -#: model_terms:ir.ui.view,arch_db:odex30_account_online_sync.portal_renew_consent -msgid "Odoo" -msgstr "أودو" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_bank_statement_line__online_account_id -msgid "Online Account" -msgstr "حساب عبر الإنترنت " - -#. module: odex30_account_online_sync -#: model_terms:ir.ui.view,arch_db:odex30_account_online_sync.account_online_link_view_form -msgid "Online Accounts" -msgstr "حسابات عبر الإنترنت " - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_online_account__online_identifier -msgid "Online Identifier" -msgstr "معرف عبر الإنترنت " - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_journal__next_link_synchronization -msgid "Online Link Next synchronization" -msgstr "ربط المزامنة التالية عبر الإنترنت " - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_bank_statement_line__online_partner_information -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_res_partner__online_partner_information -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_res_users__online_partner_information -msgid "Online Partner Information" -msgstr "معلومات الشريك عبر الإنترنت " - -#. module: odex30_account_online_sync -#. odoo-python -#: code:addons/odex30_account_online_sync/models/account_journal.py:0 -#: model:ir.actions.act_window,name:odex30_account_online_sync.action_account_online_link_form -#: model:ir.ui.menu,name:odex30_account_online_sync.menu_action_online_link_account -#: model_terms:ir.actions.act_window,help:odex30_account_online_sync.action_account_online_link_form -msgid "Online Synchronization" -msgstr "المزامنة عبر الإنترنت " - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_bank_statement_line__online_transaction_identifier -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_bank_statement_line_transient__online_transaction_identifier -msgid "Online Transaction Identifier" -msgstr "معرف المعاملات عبر الإنترنت " - -#. module: odex30_account_online_sync -#. odoo-python -#: code:addons/odex30_account_online_sync/models/account_bank_statement.py:0 -msgid "Opening statement: first synchronization" -msgstr "البيان الافتتاحي: المزامنة الأولى" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_bank_statement_line_transient__partner_name -msgid "Partner Name" -msgstr "اسم الشريك" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_bank_statement_line_transient__payment_ref -msgid "Payment Ref" -msgstr "مرجع الدفع " - -#. module: odex30_account_online_sync -#: model:ir.model.fields.selection,name:odex30_account_online_sync.selection__account_bank_statement_line_transient__state__pending -msgid "Pending" -msgstr "قيد الانتظار " - -#. module: odex30_account_online_sync -#: model:ir.model.fields.selection,name:odex30_account_online_sync.selection__account_online_account__fetching_status__planned -msgid "Planned" -msgstr "المخطط له " - -#. module: odex30_account_online_sync -#. odoo-python -#: code:addons/odex30_account_online_sync/wizard/account_journal_missing_transactions.py:0 -msgid "Please enter a valid Starting Date to continue." -msgstr "يرجى إدخال تاريخ بدء صالح للمتابعة. " - -#. module: odex30_account_online_sync -#. odoo-python -#: code:addons/odex30_account_online_sync/models/account_online.py:0 -msgid "Please reconnect your online account." -msgstr "الرجاء إعادة توصيل حسابك عبر الإنترنت. " - -#. module: odex30_account_online_sync -#. odoo-python -#: code:addons/odex30_account_online_sync/wizard/account_bank_statement_line.py:0 -msgid "Please select first the transactions you wish to import." -msgstr "يرجى أولاً تحديد المعاملات التي ترغب في استيرادها. " - -#. module: odex30_account_online_sync -#: model:ir.model.fields.selection,name:odex30_account_online_sync.selection__account_bank_statement_line_transient__state__posted -msgid "Posted" -msgstr "مُرحّل " - -#. module: odex30_account_online_sync -#: model_terms:ir.ui.view,arch_db:odex30_account_online_sync.missing_bank_statement_line_search -msgid "Posted Transactions" -msgstr "المعاملات التي تم ترحيلها " - -#. module: odex30_account_online_sync -#: model:ir.model.fields.selection,name:odex30_account_online_sync.selection__account_online_account__fetching_status__processing -msgid "Processing" -msgstr "معالجة " - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_online_link__provider_data -msgid "Provider Data" -msgstr "بيانات المزود " - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_online_link__provider_type -msgid "Provider Type" -msgstr "نوع Plaid" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_online_link__rating_ids -msgid "Ratings" -msgstr "التقييمات" - -#. module: odex30_account_online_sync -#: model_terms:ir.ui.view,arch_db:odex30_account_online_sync.account_online_link_view_form -msgid "Reconnect" -msgstr "إعادة توصيل " - -#. module: odex30_account_online_sync -#. odoo-javascript -#: code:addons/odex30_account_online_sync/static/src/components/connected_until_widget/connected_until_widget.xml:0 -#: code:addons/odex30_account_online_sync/static/src/components/refresh_spin_journal_widget/refresh_spin_journal_widget.xml:0 -#: model_terms:ir.ui.view,arch_db:odex30_account_online_sync.account_journal_dashboard_inherit_online_sync -msgid "Reconnect Bank" -msgstr "إعادة توصيل البنك " - -#. module: odex30_account_online_sync -#. odoo-python -#: code:addons/odex30_account_online_sync/models/account_online.py:0 -msgid "Redirect" -msgstr "إعادة توجيه" - -#. module: odex30_account_online_sync -#. odoo-javascript -#: code:addons/odex30_account_online_sync/static/src/components/refresh_spin_journal_widget/refresh_spin_journal_widget.xml:0 -msgid "Refresh" -msgstr "تحديث " - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_online_link__refresh_token -msgid "Refresh Token" -msgstr "تحديث الرمز" - -#. module: odex30_account_online_sync -#. odoo-python -#: code:addons/odex30_account_online_sync/models/account_journal.py:0 -msgid "Report Issue" -msgstr "إبلاغ عن إساءة " - -#. module: odex30_account_online_sync -#. odoo-python -#: code:addons/odex30_account_online_sync/models/account_online.py:0 -msgid "Report issue" -msgstr "إبلاغ عن مشكلة " - -#. module: odex30_account_online_sync -#: model:ir.model.fields,help:odex30_account_online_sync.field_account_online_link__client_id -msgid "Represent a link for a given user towards a banking institution" -msgstr "تقديم رابط لمستخدم معين إلى منشأة بنكية " - -#. module: odex30_account_online_sync -#: model_terms:ir.ui.view,arch_db:odex30_account_online_sync.account_online_link_view_form -msgid "Reset" -msgstr "إعادة الضبط " - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_online_link__activity_user_id -msgid "Responsible User" -msgstr "المستخدم المسؤول" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_online_link__message_has_sms_error -msgid "SMS Delivery error" -msgstr "خطأ في تسليم الرسائل النصية القصيرة " - -#. module: odex30_account_online_sync -#. odoo-javascript -#: code:addons/odex30_account_online_sync/static/src/components/bank_configure/bank_configure.xml:0 -msgid "Search over" -msgstr "البحث " - -#. module: odex30_account_online_sync -#: model_terms:ir.ui.view,arch_db:odex30_account_online_sync.portal_renew_consent -msgid "" -"Security Tip: always check the domain name of this page, before clicking on " -"the button." -msgstr "" -"نصيحة للأمان: تحقق دائماً من اسم النطاق لهذه الصفحة، عن طريق الضغط على الزر." -" " - -#. module: odex30_account_online_sync -#. odoo-javascript -#: code:addons/odex30_account_online_sync/static/src/components/refresh_spin_journal_widget/refresh_spin_journal_widget.xml:0 -msgid "See error" -msgstr "انظر إلى الخطأ " - -#. module: odex30_account_online_sync -#. odoo-python -#: code:addons/odex30_account_online_sync/models/account_online.py:0 -#: model_terms:ir.ui.view,arch_db:odex30_account_online_sync.view_account_bank_selection_form_wizard -msgid "Select a Bank Account" -msgstr "تحديد حساب بنكي " - -#. module: odex30_account_online_sync -#: model_terms:ir.ui.view,arch_db:odex30_account_online_sync.view_account_bank_selection_form_wizard -msgid "Select the" -msgstr "قم بتحديد " - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_bank_selection__selected_account -msgid "Selected Account" -msgstr "الحساب المحدد " - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_bank_statement_line_transient__sequence -msgid "Sequence" -msgstr "تسلسل " - -#. module: odex30_account_online_sync -#: model:ir.model.fields,help:odex30_account_online_sync.field_account_online_account__account_number -msgid "Set if third party provider has the full account number" -msgstr "التعيين إذا كان لدى مزود الطرف الثالث رقم الحساب الكامل " - -#. module: odex30_account_online_sync -#. odoo-javascript -#: code:addons/odex30_account_online_sync/static/src/components/bank_configure/bank_configure.xml:0 -msgid "Setup Bank" -msgstr "إعداد البنك " - -#. module: odex30_account_online_sync -#. odoo-python -#: code:addons/odex30_account_online_sync/models/account_online.py:0 -msgid "Setup Bank Account" -msgstr "إعداد الحساب البنكي " - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_online_link__show_sync_actions -msgid "Show Sync Actions" -msgstr "عرض إجراءات المزامنة " - -#. module: odex30_account_online_sync -#. odoo-javascript -#: code:addons/odex30_account_online_sync/static/src/components/views/account_online_authorization_kanban_controller.xml:0 -msgid "Some transactions" -msgstr "بعض المعاملات " - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_duplicate_transaction_wizard__date -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_missing_transaction_wizard__date -msgid "Starting Date" -msgstr "تاريخ البدء" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_bank_statement_line_transient__state -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_journal__account_online_link_state -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_online_link__state -msgid "State" -msgstr "الحالة " - -#. module: odex30_account_online_sync -#: model:ir.model.fields,help:odex30_account_online_sync.field_account_online_link__activity_state -msgid "" -"Status based on activities\n" -"Overdue: Due date is already passed\n" -"Today: Activity date is today\n" -"Planned: Future activities." -msgstr "" -"الأنشطة المعتمدة على الحالة\n" -"المتأخرة: تاريخ الاستحقاق مر\n" -"اليوم: تاريخ النشاط هو اليوم\n" -"المخطط: الأنشطة المستقبلية." - -#. module: odex30_account_online_sync -#: model_terms:ir.ui.view,arch_db:odex30_account_online_sync.portal_renew_consent -msgid "Thank You!" -msgstr "شكرا لك! " - -#. module: odex30_account_online_sync -#. odoo-python -#: code:addons/odex30_account_online_sync/models/account_online.py:0 -msgid "The consent for the selected account has expired." -msgstr "انتهت صلاحية الإذن للحساب المحدد. " - -#. module: odex30_account_online_sync -#. odoo-python -#: code:addons/odex30_account_online_sync/models/account_online.py:0 -msgid "" -"The online synchronization service is not available at the moment. Please " -"try again later." -msgstr "" -"خدمة المزامنة عبر الإنترنت غير متوفرة في الوقت الحالي. الرجاء المحاولة " -"مجدداً لاحقاً " - -#. module: odex30_account_online_sync -#: model:ir.model.fields,help:odex30_account_online_sync.field_account_online_link__provider_type -msgid "Third Party Provider" -msgstr "مزود الطرف الثالث " - -#. module: odex30_account_online_sync -#: model_terms:ir.ui.view,arch_db:odex30_account_online_sync.account_duplicate_transaction_wizard_view_form -msgid "" -"This action will delete all selected transactions. Are you sure you want to " -"proceed?" -msgstr "" -"سيؤدي هذا الإجراء إلى حذف كافة المعاملات المحددة. هل أنت متأكد أنك ترغب في " -"المتابعة؟ " - -#. module: odex30_account_online_sync -#: model_terms:ir.ui.view,arch_db:odex30_account_online_sync.account_online_link_view_form -msgid "This button will reset the fetching status" -msgstr "سيؤدي هذا الزر إلى إعادة ضبط حالة عملية البحث " - -#. module: odex30_account_online_sync -#. odoo-python -#: code:addons/odex30_account_online_sync/models/account_online.py:0 -msgid "" -"This version of Odoo appears to be outdated and does not support the '%s' " -"sync mode. Installing the latest update might solve this." -msgstr "" -"يبدو أن هذه النسخة من أودو قديمة ولا تدعم وضع المزامنة '%s'. قد يحل تثبيت " -"آخر تحديث هذه المشكلة. " - -#. module: odex30_account_online_sync -#: model_terms:ir.actions.act_window,help:odex30_account_online_sync.action_account_online_link_form -msgid "" -"To create a synchronization with your banking institution,
\n" -" please click on Add a Bank Account." -msgstr "" -"لإنشاء مزامنة مع المنشأة البنكية،
\n" -" يرجى الضغط على إضافة حساب بنكي. " - -#. module: odex30_account_online_sync -#: model:ir.model.fields,help:odex30_account_online_sync.field_account_online_link__access_token -msgid "Token used to access API." -msgstr "الرمز المستخدَم للوصول إلى الواجهة البرمجية للتطبيق. " - -#. module: odex30_account_online_sync -#: model:ir.model.fields,help:odex30_account_online_sync.field_account_online_link__refresh_token -msgid "Token used to sign API request, Never disclose it" -msgstr "" -"الرمز المستخدَم للتوقيع على طلب الواجهة البرمجية للتطبيق، لا تقم بالإفصاح " -"عنه أبداً " - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_duplicate_transaction_wizard__transaction_ids -msgid "Transaction" -msgstr "معاملة" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_bank_statement_line_transient__transaction_details -msgid "Transaction Details" -msgstr "تفاصيل المعاملة " - -#. module: odex30_account_online_sync -#: model_terms:ir.ui.view,arch_db:odex30_account_online_sync.account_duplicate_transaction_wizard_view_form -msgid "Transactions" -msgstr "المعاملات " - -#. module: odex30_account_online_sync -#: model:ir.model,name:odex30_account_online_sync.model_account_bank_statement_line_transient -msgid "Transient model for bank statement line" -msgstr "Transient model for bank statement line" - -#. module: odex30_account_online_sync -#: model:ir.model.fields,help:odex30_account_online_sync.field_account_online_link__has_unlinked_accounts -msgid "" -"True if that connection still has accounts that are not linked to an Odoo " -"journal" -msgstr "" -"تكون القيمة صحيحة إذا كان الاتصال لا يزال يحتوي على حسابات غير مرتبطة بدفتر " -"يومية أودو " - -#. module: odex30_account_online_sync -#: model:ir.model.fields,help:odex30_account_online_sync.field_account_online_link__activity_exception_decoration -msgid "Type of the exception activity on record." -msgstr "نوع النشاط المستثنى في السجل. " - -#. module: odex30_account_online_sync -#: model_terms:ir.ui.view,arch_db:odex30_account_online_sync.account_online_link_view_form -msgid "Update Credentials" -msgstr "تحديث بيانات الاعتماد" - -#. module: odex30_account_online_sync -#: model:ir.model.fields.selection,name:odex30_account_online_sync.selection__account_online_account__fetching_status__waiting -msgid "Waiting" -msgstr "قيد الانتظار " - -#. module: odex30_account_online_sync -#: model:ir.model.fields,field_description:odex30_account_online_sync.field_account_online_link__website_message_ids -msgid "Website Messages" -msgstr "رسائل الموقع الإلكتروني " - -#. module: odex30_account_online_sync -#: model:ir.model.fields,help:odex30_account_online_sync.field_account_online_link__website_message_ids -msgid "Website communication history" -msgstr "سجل تواصل الموقع الإلكتروني " - -#. module: odex30_account_online_sync -#: model:ir.model,name:odex30_account_online_sync.model_account_duplicate_transaction_wizard -msgid "Wizard for duplicate transactions" -msgstr "معالج للمعاملات المكررة " - -#. module: odex30_account_online_sync -#: model:ir.model,name:odex30_account_online_sync.model_account_missing_transaction_wizard -msgid "Wizard for missing transactions" -msgstr "معالج للمعاملات المفقودة " - -#. module: odex30_account_online_sync -#. odoo-javascript -#: code:addons/odex30_account_online_sync/static/src/components/transient_bank_statement_line_list_view/transient_bank_statement_line_list_view.xml:0 -msgid "" -"You are importing transactions before the creation of your online synchronization\n" -" (" -msgstr "" -"أنت تقوم باستيراد المعاملات قبل إنشاء المزامنة عبر الإنترنت\n" -" (" - -#. module: odex30_account_online_sync -#. odoo-python -#: code:addons/odex30_account_online_sync/models/account_online.py:0 -msgid "You can contact Odoo support" -msgstr "يمكنك التواصل مع فريق الدعم لدى أودو " - -#. module: odex30_account_online_sync -#. odoo-python -#: code:addons/odex30_account_online_sync/models/account_journal.py:0 -msgid "You can only execute this action for bank-synchronized journals." -msgstr "لا يمكنك تنفيذ هذا الإجراء إلا لدفاتر اليومية المتزامنة مع البنك. " - -#. module: odex30_account_online_sync -#. odoo-python -#: code:addons/odex30_account_online_sync/models/account_journal.py:0 -#: code:addons/odex30_account_online_sync/wizard/account_journal_missing_transactions.py:0 -msgid "" -"You can't find missing transactions for a journal that isn't connected." -msgstr "لا يمكنك العثور على المعاملات المفقودة لدفتر يومية غير متصل. " - -#. module: odex30_account_online_sync -#. odoo-python -#: code:addons/odex30_account_online_sync/models/account_journal.py:0 -#: code:addons/odex30_account_online_sync/models/account_online.py:0 -msgid "You cannot have two journals associated with the same Online Account." -msgstr "لا يمكن أن يكون لديك حسابان مرتبطان بنفس الحساب عبر الإنترنت. " - -#. module: odex30_account_online_sync -#. odoo-python -#: code:addons/odex30_account_online_sync/wizard/account_bank_statement_line.py:0 -msgid "You cannot import pending transactions." -msgstr "لا يمكنك استيراد المعاملات المعلقة. " - -#. module: odex30_account_online_sync -#. odoo-javascript -#: code:addons/odex30_account_online_sync/static/src/components/transient_bank_statement_line_list_view/transient_bank_statement_line_list_view.xml:0 -msgid "You have" -msgstr "لديك" - -#. module: odex30_account_online_sync -#. odoo-python -#: code:addons/odex30_account_online_sync/wizard/account_journal_missing_transactions.py:0 -msgid "You have to select one journal to continue." -msgstr "عليك تحديد دفتر يومية واحد للاستمرار. " - -#. module: odex30_account_online_sync -#: model:mail.template,subject:odex30_account_online_sync.email_template_sync_reminder -msgid "Your bank connection is expiring soon" -msgstr "سوف ينتهي اتصال البنك الخاص بك قريباً " - -#. module: odex30_account_online_sync -#: model_terms:ir.ui.view,arch_db:odex30_account_online_sync.view_account_bank_selection_form_wizard -msgid "account to connect:" -msgstr "الحساب لربطه: " - -#. module: odex30_account_online_sync -#. odoo-javascript -#: code:addons/odex30_account_online_sync/static/src/components/bank_configure/bank_configure.xml:0 -msgid "banks" -msgstr "البنوك " - -#. module: odex30_account_online_sync -#. odoo-javascript -#: code:addons/odex30_account_online_sync/static/src/components/transient_bank_statement_line_list_view/transient_bank_statement_line_list_view.xml:0 -msgid "entries" -msgstr "القيود" - -#. module: odex30_account_online_sync -#. odoo-javascript -#: code:addons/odex30_account_online_sync/static/src/components/bank_configure/bank_configure.xml:0 -msgid "loading..." -msgstr "جاري التحميل..." - -#. module: odex30_account_online_sync -#. odoo-javascript -#: code:addons/odex30_account_online_sync/static/src/components/views/account_online_authorization_kanban_controller.xml:0 -msgid "may be duplicates." -msgstr "قد تكون عناك نُسخ مكررة " - -#. module: odex30_account_online_sync -#: model_terms:ir.ui.view,arch_db:odex30_account_online_sync.portal_renew_consent -msgid "on" -msgstr "في" - -#. module: odex30_account_online_sync -#: model:ir.model,name:odex30_account_online_sync.model_account_online_account -msgid "representation of an online bank account" -msgstr "تمثيل لحساب بنكي عبر الإنترنت " - -#. module: odex30_account_online_sync -#. odoo-javascript -#: code:addons/odex30_account_online_sync/static/src/components/refresh_spin_journal_widget/refresh_spin_journal_widget.xml:0 -msgid "transactions fetched" -msgstr "تم جلب المعاملات " - -#. module: odex30_account_online_sync -#. odoo-javascript -#: code:addons/odex30_account_online_sync/static/src/components/transient_bank_statement_line_list_view/transient_bank_statement_line_list_view.xml:0 -msgid "" -"within this period that were not created using the online synchronization. " -"This might cause duplicate entries." -msgstr "" -"خلال هذه الفترة والتي لم يتم إنشاؤها باستخدام المزامنة عبر الإنترنت. قد " -"يتسبب هذا في استنساخ القيود. " diff --git a/dev_odex30_accounting/odex30_account_online_sync/models/__init__.py b/dev_odex30_accounting/odex30_account_online_sync/models/__init__.py deleted file mode 100644 index fa59fc5..0000000 --- a/dev_odex30_accounting/odex30_account_online_sync/models/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -# -*- coding: utf-8 -*- - -from . import account_bank_statement -from . import account_journal -from . import account_online -from . import company -from . import mail_activity_type -from . import partner -from . import bank_rec_widget diff --git a/dev_odex30_accounting/odex30_account_online_sync/models/__pycache__/__init__.cpython-311.pyc b/dev_odex30_accounting/odex30_account_online_sync/models/__pycache__/__init__.cpython-311.pyc deleted file mode 100644 index 807c970..0000000 Binary files a/dev_odex30_accounting/odex30_account_online_sync/models/__pycache__/__init__.cpython-311.pyc and /dev/null differ diff --git a/dev_odex30_accounting/odex30_account_online_sync/models/__pycache__/account_bank_statement.cpython-311.pyc b/dev_odex30_accounting/odex30_account_online_sync/models/__pycache__/account_bank_statement.cpython-311.pyc deleted file mode 100644 index 161301f..0000000 Binary files a/dev_odex30_accounting/odex30_account_online_sync/models/__pycache__/account_bank_statement.cpython-311.pyc and /dev/null differ diff --git a/dev_odex30_accounting/odex30_account_online_sync/models/__pycache__/account_journal.cpython-311.pyc b/dev_odex30_accounting/odex30_account_online_sync/models/__pycache__/account_journal.cpython-311.pyc deleted file mode 100644 index 65ca1e2..0000000 Binary files a/dev_odex30_accounting/odex30_account_online_sync/models/__pycache__/account_journal.cpython-311.pyc and /dev/null differ diff --git a/dev_odex30_accounting/odex30_account_online_sync/models/__pycache__/account_online.cpython-311.pyc b/dev_odex30_accounting/odex30_account_online_sync/models/__pycache__/account_online.cpython-311.pyc deleted file mode 100644 index eb8eff3..0000000 Binary files a/dev_odex30_accounting/odex30_account_online_sync/models/__pycache__/account_online.cpython-311.pyc and /dev/null differ diff --git a/dev_odex30_accounting/odex30_account_online_sync/models/__pycache__/bank_rec_widget.cpython-311.pyc b/dev_odex30_accounting/odex30_account_online_sync/models/__pycache__/bank_rec_widget.cpython-311.pyc deleted file mode 100644 index 23f8bc4..0000000 Binary files a/dev_odex30_accounting/odex30_account_online_sync/models/__pycache__/bank_rec_widget.cpython-311.pyc and /dev/null differ diff --git a/dev_odex30_accounting/odex30_account_online_sync/models/__pycache__/company.cpython-311.pyc b/dev_odex30_accounting/odex30_account_online_sync/models/__pycache__/company.cpython-311.pyc deleted file mode 100644 index 9dba055..0000000 Binary files a/dev_odex30_accounting/odex30_account_online_sync/models/__pycache__/company.cpython-311.pyc and /dev/null differ diff --git a/dev_odex30_accounting/odex30_account_online_sync/models/__pycache__/mail_activity_type.cpython-311.pyc b/dev_odex30_accounting/odex30_account_online_sync/models/__pycache__/mail_activity_type.cpython-311.pyc deleted file mode 100644 index 2aabcb1..0000000 Binary files a/dev_odex30_accounting/odex30_account_online_sync/models/__pycache__/mail_activity_type.cpython-311.pyc and /dev/null differ diff --git a/dev_odex30_accounting/odex30_account_online_sync/models/__pycache__/odoofin_auth.cpython-311.pyc b/dev_odex30_accounting/odex30_account_online_sync/models/__pycache__/odoofin_auth.cpython-311.pyc deleted file mode 100644 index 3a6a04c..0000000 Binary files a/dev_odex30_accounting/odex30_account_online_sync/models/__pycache__/odoofin_auth.cpython-311.pyc and /dev/null differ diff --git a/dev_odex30_accounting/odex30_account_online_sync/models/__pycache__/partner.cpython-311.pyc b/dev_odex30_accounting/odex30_account_online_sync/models/__pycache__/partner.cpython-311.pyc deleted file mode 100644 index a501c7f..0000000 Binary files a/dev_odex30_accounting/odex30_account_online_sync/models/__pycache__/partner.cpython-311.pyc and /dev/null differ diff --git a/dev_odex30_accounting/odex30_account_online_sync/models/account_bank_statement.py b/dev_odex30_accounting/odex30_account_online_sync/models/account_bank_statement.py deleted file mode 100644 index bade26e..0000000 --- a/dev_odex30_accounting/odex30_account_online_sync/models/account_bank_statement.py +++ /dev/null @@ -1,114 +0,0 @@ -# -*- coding: utf-8 -*- -import threading -import time -import json - -from odoo import api, fields, models, SUPERUSER_ID, tools, _ -from odoo.tools import date_utils -from odoo.exceptions import UserError, ValidationError - -STATEMENT_LINE_CREATION_BATCH_SIZE = 500 # When importing transactions, batch the process to commit after importing batch_size - - -class AccountBankStatementLine(models.Model): - _inherit = 'account.bank.statement.line' - - online_transaction_identifier = fields.Char("Online Transaction Identifier", readonly=True) - online_partner_information = fields.Char(readonly=True) - online_account_id = fields.Many2one(comodel_name='account.online.account', readonly=True) - online_link_id = fields.Many2one( - comodel_name='account.online.link', - related='online_account_id.account_online_link_id', - store=True, - readonly=True, - ) - - @api.model_create_multi - def create(self, vals_list): - """ - Some transactions can be marked as "Zero Balancing", - which is a transaction used at the end of the day to summarize all the transactions - of the day. As we already manage the details of all the transactions, this one is not - useful and moreover create duplicates. To deal with that, we cancel the move and so - the bank statement line. - """ - # EXTEND account - bank_statement_lines = super().create(vals_list) - moves_to_cancel = self.env['account.move'] - for bank_statement_line in bank_statement_lines: - transaction_details = json.loads(bank_statement_line.transaction_details) if bank_statement_line.transaction_details else {} - if not transaction_details.get('is_zero_balancing'): - continue - moves_to_cancel |= bank_statement_line.move_id - moves_to_cancel.button_cancel() - - return bank_statement_lines - - @api.model - def _online_sync_bank_statement(self, transactions, online_account): - """ - build bank statement lines from a list of transaction and post messages is also post in the online_account of the journal. - :param transactions: A list of transactions that will be created. - The format is : [{ - 'id': online id, (unique ID for the transaction) - 'date': transaction date, (The date of the transaction) - 'name': transaction description, (The description) - 'amount': transaction amount, (The amount of the transaction. Negative for debit, positive for credit) - }, ...] - :param online_account: The online account for this statement - Return: The number of imported transaction for the journal - """ - start_time = time.time() - lines_to_reconcile = self.env['account.bank.statement.line'] - try: - for journal in online_account.journal_ids: - # Since the synchronization succeeded, set it as the bank_statements_source of the journal - journal.sudo().write({'bank_statements_source': 'online_sync'}) - if not transactions: - continue - - sorted_transactions = sorted(transactions, key=lambda transaction: transaction['date']) - total = self.env.context.get('transactions_total') or sum([transaction['amount'] for transaction in transactions]) - - # For first synchronization, an opening line is created to fill the missing bank statement data - any_st_line = self.search_count([('journal_id', '=', journal.id)], limit=1) - journal_currency = journal.currency_id or journal.company_id.currency_id - # If there are neither statement and the ending balance != 0, we create an opening bank statement at the day of the oldest transaction. - # We set the sequence to >1 to ensure the computed internal_index will force its display before any other statement with the same date. - if not any_st_line and not journal_currency.is_zero(online_account.balance - total): - opening_st_line = self.with_context(skip_statement_line_cron_trigger=True).create({ - 'date': sorted_transactions[0]['date'], - 'journal_id': journal.id, - 'payment_ref': _("Opening statement: first synchronization"), - 'amount': online_account.balance - total, - 'sequence': 2, - }) - lines_to_reconcile += opening_st_line - - filtered_transactions = online_account._get_filtered_transactions(sorted_transactions) - - do_commit = not (hasattr(threading.current_thread(), 'testing') and threading.current_thread().testing) - if filtered_transactions: - # split transactions import in batch and commit after each batch except in testing mode - for index in range(0, len(filtered_transactions), STATEMENT_LINE_CREATION_BATCH_SIZE): - lines_to_reconcile += self.with_user(SUPERUSER_ID).with_company(journal.company_id).with_context(skip_statement_line_cron_trigger=True).create(filtered_transactions[index:index + STATEMENT_LINE_CREATION_BATCH_SIZE]) - if do_commit: - self.env.cr.commit() - # Set last sync date as the last transaction date - journal.account_online_account_id.sudo().write({'last_sync': filtered_transactions[-1]['date']}) - - if lines_to_reconcile: - # 'limit_time_real_cron' defaults to -1. - # Manual fallback applied for non-POSIX systems where this key is disabled (set to None). - cron_limit_time = tools.config['limit_time_real_cron'] or -1 - limit_time = (cron_limit_time if cron_limit_time > 0 else 180) - (time.time() - start_time) - if limit_time > 0: - lines_to_reconcile._cron_try_auto_reconcile_statement_lines(limit_time=limit_time) - # Catch any configuration error that would prevent creating the entries, reset fetching_status flag and re-raise the error - # Otherwise flag is never reset and user is under the impression that we are still fetching transactions - except (UserError, ValidationError) as e: - self.env.cr.rollback() - online_account.account_online_link_id._log_information('error', subject=_("Error"), message=str(e)) - self.env.cr.commit() - raise - return lines_to_reconcile diff --git a/dev_odex30_accounting/odex30_account_online_sync/models/account_journal.py b/dev_odex30_accounting/odex30_account_online_sync/models/account_journal.py deleted file mode 100644 index afb0777..0000000 --- a/dev_odex30_accounting/odex30_account_online_sync/models/account_journal.py +++ /dev/null @@ -1,380 +0,0 @@ -# -*- coding: utf-8 -*- - -import logging -import requests -from dateutil.relativedelta import relativedelta -from requests.exceptions import RequestException, Timeout - -from odoo import api, fields, models, tools, _ -from odoo.exceptions import UserError, ValidationError, RedirectWarning -from odoo.tools import SQL - -_logger = logging.getLogger(__name__) - - -class AccountJournal(models.Model): - _inherit = "account.journal" - - def __get_bank_statements_available_sources(self): - rslt = super(AccountJournal, self).__get_bank_statements_available_sources() - rslt.append(("online_sync", _("Online Synchronization"))) - return rslt - - next_link_synchronization = fields.Datetime("Online Link Next synchronization", related='account_online_link_id.next_refresh') - expiring_synchronization_date = fields.Date(related='account_online_link_id.expiring_synchronization_date') - expiring_synchronization_due_day = fields.Integer(compute='_compute_expiring_synchronization_due_day') - account_online_account_id = fields.Many2one('account.online.account', copy=False, ondelete='set null') - account_online_link_id = fields.Many2one('account.online.link', related='account_online_account_id.account_online_link_id', readonly=True, store=True) - account_online_link_state = fields.Selection(related="account_online_link_id.state", readonly=True) - renewal_contact_email = fields.Char( - string='Connection Requests', - help='Comma separated list of email addresses to send consent renewal notifications 15, 3 and 1 days before expiry', - default=lambda self: self.env.user.email, - ) - online_sync_fetching_status = fields.Selection(related="account_online_account_id.fetching_status", readonly=True) - - def write(self, vals): - # When changing the bank_statement_source, unlink the connection if there is any - if 'bank_statements_source' in vals and vals.get('bank_statements_source') != 'online_sync': - for journal in self: - if journal.bank_statements_source == 'online_sync': - # unlink current connection - vals['account_online_account_id'] = False - journal.account_online_link_id.has_unlinked_accounts = True - return super().write(vals) - - @api.depends('expiring_synchronization_date') - def _compute_expiring_synchronization_due_day(self): - for record in self: - if record.expiring_synchronization_date: - due_day_delta = record.expiring_synchronization_date - fields.Date.context_today(record) - record.expiring_synchronization_due_day = due_day_delta.days - else: - record.expiring_synchronization_due_day = 0 - - def _fill_bank_cash_dashboard_data(self, dashboard_data): - super()._fill_bank_cash_dashboard_data(dashboard_data) - # Caching data to avoid one call per journal - self.browse(list(dashboard_data.keys())).fetch(['type', 'account_online_account_id']) - for journal_id, journal_data in dashboard_data.items(): - journal = self.browse(journal_id) - journal_data['display_connect_bank_in_dashboard'] = journal.type in ('bank', 'credit') \ - and not journal.account_online_account_id \ - and journal.company_id.id == self.env.company.id - - @api.constrains('account_online_account_id') - def _check_account_online_account_id(self): - for journal in self: - if len(journal.account_online_account_id.journal_ids) > 1: - raise ValidationError(_('You cannot have two journals associated with the same Online Account.')) - - def _fetch_online_transactions(self): - for journal in self: - try: - journal.account_online_link_id._pop_connection_state_details(journal=journal) - journal.manual_sync() - # for cron jobs it is usually recommended committing after each iteration, - # so that a later error or job timeout doesn't discard previous work - self.env.cr.commit() - except (UserError, RedirectWarning): - # We need to rollback here otherwise the next iteration will still have the error when trying to commit - self.env.cr.rollback() - - def fetch_online_sync_favorite_institutions(self): - self.ensure_one() - timeout = int(self.env['ir.config_parameter'].sudo().get_param('odex30_account_online_sync.request_timeout')) or 60 - endpoint_url = self.env['account.online.link']._get_odoofin_url('/proxy/v1/get_dashboard_institutions') - params = {'country': self.sudo().company_id.account_fiscal_country_id.code, 'limit': 28} - try: - resp = requests.post(endpoint_url, json=params, timeout=timeout) - resp_dict = resp.json()['result'] - for institution in resp_dict: - if institution['picture'].startswith('/'): - institution['picture'] = self.env['account.online.link']._get_odoofin_url(institution['picture']) - return resp_dict - except (Timeout, ConnectionError, RequestException, ValueError) as e: - _logger.warning(e) - return [] - - @api.model - def _cron_fetch_waiting_online_transactions(self): - """ This method is only called when the user fetch transactions asynchronously. - We only fetch transactions on synchronizations that are in "waiting" status. - Once the synchronization is done, the status is changed for "done". - We have to that to avoid having too much logic in the same cron function to do - 2 different things. This cron should only be used for asynchronous fetchs. - """ - - # 'limit_time_real_cron' and 'limit_time_real' default respectively to -1 and 120. - # Manual fallbacks applied for non-POSIX systems where this key is disabled (set to None). - limit_time = tools.config['limit_time_real_cron'] or -1 - if limit_time <= 0: - limit_time = tools.config['limit_time_real'] or 120 - journals = self.search([ - '|', - ('online_sync_fetching_status', 'in', ('planned', 'waiting')), - '&', - ('online_sync_fetching_status', '=', 'processing'), - ('account_online_link_id.last_refresh', '<', fields.Datetime.now() - relativedelta(seconds=limit_time)), - ]) - journals.with_context(cron=True)._fetch_online_transactions() - - @api.model - def _cron_fetch_online_transactions(self): - """ This method is called by the cron (by default twice a day) to fetch (for all journals) - the new transactions. - """ - journals = self.search([('account_online_account_id', '!=', False)]) - journals.with_context(cron=True)._fetch_online_transactions() - - @api.model - def _cron_send_reminder_email(self): - for journal in self.search([('account_online_account_id', '!=', False)]): - if journal.expiring_synchronization_due_day in {1, 3, 15}: - journal.action_send_reminder() - - def manual_sync(self): - self.ensure_one() - if self.account_online_link_id: - account = self.account_online_account_id - return self.account_online_link_id._fetch_transactions(accounts=account) - - def unlink(self): - """ - Override of the unlink method. - That's useful to unlink account.online.account too. - """ - if self.account_online_account_id: - self.account_online_account_id.unlink() - return super(AccountJournal, self).unlink() - - def action_configure_bank_journal(self): - """ - Override the "action_configure_bank_journal" and change the flow for the - "Configure" button in dashboard. - """ - self.ensure_one() - return self.env['account.online.link'].action_new_synchronization() - - def action_open_account_online_link(self): - self.ensure_one() - return { - 'type': 'ir.actions.act_window', - 'name': self.account_online_link_id.name, - 'res_model': 'account.online.link', - 'target': 'main', - 'view_mode': 'form', - 'views': [[False, 'form']], - 'res_id': self.account_online_link_id.id, - } - - def action_extend_consent(self): - """ - Extend the consent of the user by redirecting him to update his credentials - """ - self.ensure_one() - return self.account_online_link_id._open_iframe( - mode='updateCredentials', - include_param={ - 'account_online_identifier': self.account_online_account_id.online_identifier, - }, - ) - - def action_reconnect_online_account(self): - self.ensure_one() - return self.account_online_link_id.action_reconnect_account() - - def action_send_reminder(self): - self.ensure_one() - self._portal_ensure_token() - template = self.env.ref('odex30_account_online_sync.email_template_sync_reminder') - subtype = self.env.ref('odex30_account_online_sync.bank_sync_consent_renewal') - self.message_post_with_source(source_ref=template, subtype_id=subtype.id) - - def action_open_missing_transaction_wizard(self): - """ This method allows to open the wizard to fetch the missing - transactions and the pending ones. - Depending on where the function is called, we'll receive - one journal or none of them. - If we receive more or less than one journal, we do not set - it on the wizard, the user should select it by himself. - - :return: An action opening the wizard. - """ - journal_id = None - if len(self) == 1: - if not self.account_online_account_id or self.account_online_link_state != 'connected': - raise UserError(_("You can't find missing transactions for a journal that isn't connected.")) - - journal_id = self.id - - wizard = self.env['account.missing.transaction.wizard'].create({'journal_id': journal_id}) - return { - 'name': _("Find Missing Transactions"), - 'type': 'ir.actions.act_window', - 'res_model': 'account.missing.transaction.wizard', - 'res_id': wizard.id, - 'views': [(False, 'form')], - 'target': 'new', - } - - def action_open_duplicate_transaction_wizard(self, from_date=None): - """ This method allows to open the wizard to find duplicate transactions. - :param from_date: date from with we must check for duplicates. - - :return: An action opening the wizard. - """ - wizard = self.env['account.duplicate.transaction.wizard'].create({ - 'journal_id': self.id if len(self) == 1 else None, - **({'date': from_date} if from_date else {}), - }) - return wizard._get_records_action(name=_("Find Duplicate Transactions")) - - def _has_duplicate_transactions(self, date_from): - """ Has any transaction with - - same amount & - - same date & - - same account number - We do not check on online_transaction_identifier because this is called after the fetch - where transitions would already have been filtered on existing online_transaction_identifier. - - :param from_date: date from with we must check for duplicates. - """ - self.env.cr.execute(SQL.join(SQL(''), [ - self._get_duplicate_amount_date_account_transactions_query(date_from), - SQL('LIMIT 1'), - ])) - return bool(self.env.cr.rowcount) - - def _get_duplicate_transactions(self, date_from): - """Find all transaction with - - same amount & - - same date & - - same account number - or - - same transaction id - - :param from_date: date from with we must check for duplicates. - """ - query = SQL.join(SQL(''), [ - self._get_duplicate_amount_date_account_transactions_query(date_from), - SQL('UNION'), - self._get_duplicate_online_transaction_identifier_transactions_query(date_from), - SQL('ORDER BY ids'), - ]) - return [res[0] for res in self.env.execute_query(query)] - - def _get_duplicate_amount_date_account_transactions_query(self, date_from): - self.ensure_one() - return SQL(''' - SELECT ARRAY_AGG(st_line.id ORDER BY st_line.id) AS ids - FROM account_bank_statement_line st_line - JOIN account_move move ON move.id = st_line.move_id - WHERE st_line.journal_id = %(journal_id)s AND move.date >= %(date_from)s - GROUP BY st_line.currency_id, st_line.amount, st_line.account_number, move.date - HAVING count(st_line.id) > 1 - ''', - journal_id=self.id, - date_from=date_from, - ) - - def _get_duplicate_online_transaction_identifier_transactions_query(self, date_from): - return SQL(''' - SELECT ARRAY_AGG(st_line.id ORDER BY st_line.id) AS ids - FROM account_bank_statement_line st_line - JOIN account_move move ON move.id = st_line.move_id - WHERE st_line.journal_id = %(journal_id)s AND - move.date >= %(prior_date)s AND - st_line.online_transaction_identifier IS NOT NULL - GROUP BY st_line.online_transaction_identifier - HAVING count(st_line.id) > 1 AND BOOL_OR(move.date >= %(date_from)s) -- at least one date is > date_from - ''', - journal_id=self.id, - date_from=date_from, - prior_date=date_from - relativedelta(months=3), # allow 1 of duplicate statements to be older than "from" date - ) - - def action_open_dashboard_asynchronous_action(self): - """ This method allows to open action asynchronously - during the fetching process. - When a user clicks on the Fetch Transactions button in - the dashboard, we fetch the transactions asynchronously - and save connection state details on the synchronization. - This action allows the user to open the action saved in - the connection state details. - """ - self.ensure_one() - - if not self.account_online_account_id: - raise UserError(_("You can only execute this action for bank-synchronized journals.")) - - connection_state_details = self.account_online_link_id._pop_connection_state_details(journal=self) - if connection_state_details and connection_state_details.get('action'): - if connection_state_details.get('error_type') == 'redirect_warning': - self.env.cr.commit() - raise RedirectWarning(connection_state_details['error_message'], connection_state_details['action'], _('Report Issue')) - else: - return connection_state_details['action'] - - return {'type': 'ir.actions.client', 'tag': 'soft_reload'} - - def _get_journal_dashboard_data_batched(self): - dashboard_data = super()._get_journal_dashboard_data_batched() - for journal in self.filtered(lambda j: j.type in ('bank', 'credit')): - if journal.account_online_account_id: - if journal.company_id.id not in self.env.companies.ids: - continue - connection_state_details = journal.account_online_link_id._get_connection_state_details(journal=journal) - if not connection_state_details and journal.account_online_account_id.fetching_status in ('waiting', 'processing'): - connection_state_details = {'status': 'fetching'} - dashboard_data[journal.id]['connection_state_details'] = connection_state_details - dashboard_data[journal.id]['show_sync_actions'] = journal.account_online_link_id.show_sync_actions - return dashboard_data - - def get_related_connection_state_details(self): - """ This method allows JS widget to get the last connection state details - It's useful if the user wasn't on the dashboard when we send the message - by websocket that the asynchronous flow is finished. - In case we don't have a connection state details and if the fetching - status is set on "waiting" or "processing". We're returning that the sync - is currently fetching. - """ - self.ensure_one() - connection_state_details = self.account_online_link_id._get_connection_state_details(journal=self) - if not connection_state_details and self.account_online_account_id.fetching_status in ('waiting', 'processing'): - connection_state_details = {'status': 'fetching'} - return connection_state_details - - def _consume_connection_state_details(self): - self.ensure_one() - if self.account_online_link_id and self.env.user.has_group('account.group_account_manager'): - # In case we have a bank synchronization connected to the journal - # we want to remove the last connection state because it means that we - # have "mark as read" this state, and we don't want to display it again to - # the user. - self.account_online_link_id._pop_connection_state_details(journal=self) - - def open_action(self): - # Extends 'account_accountant' - if not self._context.get('action_name') and self.type == 'bank' and self.bank_statements_source == 'online_sync': - self._consume_connection_state_details() - return self.env['account.bank.statement.line']._action_open_bank_reconciliation_widget( - default_context={'search_default_journal_id': self.id}, - ) - return super().open_action() - - def action_open_reconcile(self): - # Extends 'account_accountant' - self._consume_connection_state_details() - return super().action_open_reconcile() - - def action_open_bank_transactions(self): - # Extends 'account_accountant' - self._consume_connection_state_details() - return super().action_open_bank_transactions() - - @api.model - def _toggle_asynchronous_fetching_cron(self): - cron = self.env.ref('odex30_account_online_sync.online_sync_cron_waiting_synchronization', raise_if_not_found=False) - if cron: - cron.sudo().toggle(model=self._name, domain=[('account_online_account_id', '!=', False)]) diff --git a/dev_odex30_accounting/odex30_account_online_sync/models/account_online.py b/dev_odex30_accounting/odex30_account_online_sync/models/account_online.py deleted file mode 100644 index 63554d4..0000000 --- a/dev_odex30_accounting/odex30_account_online_sync/models/account_online.py +++ /dev/null @@ -1,1170 +0,0 @@ -# -*- coding: utf-8 -*- - -import base64 -import datetime -import requests -import logging -import re -import uuid -import urllib.parse -import odoo -import odoo.release -from dateutil.relativedelta import relativedelta -from markupsafe import Markup - -from requests.exceptions import RequestException, Timeout, ConnectionError -from odoo import api, fields, models, modules, tools -from odoo.exceptions import UserError, CacheMiss, MissingError, ValidationError, RedirectWarning -from odoo.http import request -from odoo.addons.odex30_account_online_sync.models.odoofin_auth import OdooFinAuth -from odoo.tools.misc import format_amount, format_date, get_lang -from odoo.tools import _, LazyTranslate - -_lt = LazyTranslate(__name__) -_logger = logging.getLogger(__name__) -pattern = re.compile("^[a-z0-9-_]+$") -runbot_pattern = re.compile(r"^https:\/\/[a-z0-9-_]+\.[a-z0-9-_]+\.odoo\.com$") - -class OdooFinRedirectException(UserError): - """ When we need to open the iframe in a given mode. """ - - def __init__(self, message=_lt('Redirect'), mode='link'): - self.mode = mode - super().__init__(message) - -class AccountOnlineAccount(models.Model): - _name = 'account.online.account' - _description = 'representation of an online bank account' - - name = fields.Char(string="Account Name", help="Account Name as provided by third party provider") - online_identifier = fields.Char(help='Id used to identify account by third party provider', readonly=True) - balance = fields.Float(readonly=True, help='Balance of the account sent by the third party provider') - account_number = fields.Char(help='Set if third party provider has the full account number') - account_data = fields.Char(help='Extra information needed by third party provider', readonly=True) - - account_online_link_id = fields.Many2one('account.online.link', readonly=True, ondelete='cascade') - journal_ids = fields.One2many('account.journal', 'account_online_account_id', string='Journal', domain="[('type', 'in', ('bank', 'credit')), ('company_id', '=', company_id)]") - last_sync = fields.Date("Last synchronization") - company_id = fields.Many2one('res.company', related='account_online_link_id.company_id') - currency_id = fields.Many2one('res.currency') - fetching_status = fields.Selection( - selection=[ - ('planned', 'Planned'), # When all the transactions couldn't be imported in one go and is waiting for next batch - ('waiting', 'Waiting'), # When waiting for the provider to fetch the transactions - ('processing', 'Processing'), # When currently importing in odoo - ('done', 'Done'), # When every transaction have been imported in odoo - ] - ) - - inverse_balance_sign = fields.Boolean( - string="Inverse Balance Sign", - help="If checked, the balance sign will be inverted", - ) - inverse_transaction_sign = fields.Boolean( - string="Inverse Transaction Sign", - help="If checked, the transaction sign will be inverted", - ) - - @api.constrains('journal_ids') - def _check_journal_ids(self): - for online_account in self: - if len(online_account.journal_ids) > 1: - raise ValidationError(_('You cannot have two journals associated with the same Online Account.')) - - @api.model_create_multi - def create(self, vals): - result = super().create(vals) - if any(data.get('fetching_status') in {'waiting', 'processing', 'planned'} for data in vals): - self.env['account.journal']._toggle_asynchronous_fetching_cron() - return result - - def write(self, vals): - result = super().write(vals) - if vals.get('fetching_status') in {'waiting', 'processing', 'planned'}: - self.env['account.journal']._toggle_asynchronous_fetching_cron() - return result - - def unlink(self): - result = super().unlink() - self.env['account.journal']._toggle_asynchronous_fetching_cron() - return result - - def _assign_journal(self, swift_code=False): - """ - This method allows to link an online account to a journal with the following heuristics - Also, Create and assign bank & swift/bic code if odoofin returns one - If a journal is present in the context (active_model = account.journal and active_id), we assume that - We started the journey from a journal and we assign the online_account to that particular journal. - Otherwise we will create a new journal on the fly and assign the online_account to it. - If an online_account was previously set on the journal, it will be removed and deleted. - This will also set the 'online_sync' source on the journal and create an activity for the consent renewal - The date to fetch transaction will also be set and have the following value: - date of the latest statement line on the journal - or date of the fiscalyear lock date - or False (we fetch transactions as far as possible) - """ - currency_id = self.currency_id.id if not self.currency_id.is_current_company_currency else False - existing_journal = self.env['account.journal'].search([ - ('bank_acc_number', '=', self.account_number), - ('currency_id', '=', currency_id), - ('type', '=', 'bank'), - ('account_online_account_id', '=', False), - ], limit=1) - - self.ensure_one() - if (active_id := self.env.context.get('active_id')) and self.env.context.get('active_model') == 'account.journal': - journal = self.env['account.journal'].browse(active_id) - # If we already have a linked account on that journal, it means we are in the process of relinking - # it is due to an error that occured which require to redo the connection (can't fix it). - # Hence we delete the previously linked account.online.link to prevent showing multiple - # duplicate existing connections when opening the iframe - if journal.account_online_link_id: - journal.account_online_link_id.unlink() - - # Ensure the journal's currency matches the bank account's currency. - if self.currency_id.id != journal.currency_id.id: - # If the journal already has entries in a different currency, raise an error. - statement_lines_in_other_currency = self.env['account.bank.statement.line'].search_count([ - ('journal_id', '=', journal.id), - ('currency_id', 'not in', (False, self.currency_id.id)), - ], limit=1) - if statement_lines_in_other_currency: - raise UserError(_("Journal %(journal_name)s has been set up with a different currency and already has existing entries. " - "You can't link selected bank account in %(currency_name)s to it", - journal_name=journal.name, currency_name=self.currency_id.name)) - else: - # If the journal's default bank account has entries in a differente currency, silently do nothing to avoid an error. - move_lines_in_other_currency = self.env['account.move.line'].search_count([ - ('account_id', '=', journal.default_account_id.id), - ('currency_id', '!=', self.currency_id.id), - ], limit=1) - if not move_lines_in_other_currency: - # If not set yet and there are no conflicting entries, set it. - journal.sudo().currency_id = self.currency_id.id - elif existing_journal: - journal = existing_journal - else: - journal = self.env['account.journal'].create({ - 'name': self.account_number or self.display_name, - 'code': self.env['account.journal'].get_next_bank_cash_default_code('bank', self.env.company), - 'type': 'bank', - 'company_id': self.env.company.id, - 'currency_id': currency_id, - }) - - self.sudo().journal_ids = journal - - journal_vals = { - 'bank_statements_source': 'online_sync', - } - if self.account_number and not self.journal_ids.bank_acc_number: - journal_vals['bank_acc_number'] = self.account_number - self.journal_ids.sudo().write(journal_vals) - # Update connection status and get consent expiration date and create an activity on related journal - self.account_online_link_id._update_connection_status() - - # Set last_sync date (date of latest statement or one day after accounting lock date or False) - lock_date = self.env.company._get_user_fiscal_lock_date(journal) - last_sync = lock_date + relativedelta(days=1) if lock_date and lock_date > datetime.date.min else None - bnk_stmt_line = self.env['account.bank.statement.line'].search([('journal_id', 'in', self.journal_ids.ids)], order="date desc", limit=1) - if bnk_stmt_line: - last_sync = bnk_stmt_line.date - self.last_sync = last_sync - - if swift_code: - if self.journal_ids.bank_account_id.bank_id: - if not self.journal_ids.bank_account_id.bank_id.bic: - self.journal_ids.bank_account_id.bank_id.bic = swift_code - else: - bank_rec = self.env['res.bank'].search([('bic', '=', swift_code)], limit=1) - if not bank_rec: - bank_rec = self.env['res.bank'].create({'name': self.account_online_link_id.display_name, 'bic': swift_code}) - self.journal_ids.bank_account_id.bank_id = bank_rec.id - - def _refresh(self): - """ - This method is called on an online_account in order to check the current refresh status of the - account. If we are in manual mode and if the provider allows it, this will also trigger a - manual refresh on the provider side. Call to /proxy/v1/refresh will return a boolean - telling us if the refresh was successful or not. When not successful, we should avoid - trying to fetch transactions. Cases where we can receive an unsuccessful response are as follow - (non exhaustive list) - - Another refresh was made too early and provider/bank limit the number of refresh allowed - - Provider is in the process of importing the transactions so we should wait until he has - finished before fetching them in Odoo - :return: True if provider has refreshed the account and we can start fetching transactions - """ - data = {'account_id': self.online_identifier} - while True: - # While this is kind of a bad practice to do, it can happen that provider_data/account_data change between - # 2 calls, the reason is that those field contains the encrypted information needed to access the provider - # and first call can result in an error due to the encrypted token inside provider_data being expired for example. - # In such a case, we renew the token with the provider and send back the newly encrypted token inside provider_data - # which result in the information having changed, henceforth why those fields are passed at every loop. - data.update({ - 'provider_data': self.account_online_link_id.provider_data, - 'account_data': self.account_data, - 'fetching_status': self.fetching_status, - }) - resp_json = self.account_online_link_id._fetch_odoo_fin('/proxy/v1/refresh', data=data) - if resp_json.get('account_data'): - self.account_data = resp_json['account_data'] - currently_fetching = resp_json.get('currently_fetching') - success = resp_json.get('success', True) - if currently_fetching: - # Provider has not finished fetching transactions, set status to waiting - self.fetching_status = 'waiting' - if not resp_json.get('next_data'): - break - data['next_data'] = resp_json.get('next_data') or {} - return {'success': not currently_fetching and success, 'data': resp_json.get('data', {})} - - def _retrieve_transactions(self, date=None, include_pendings=False): - last_stmt_line = self.env['account.bank.statement.line'].search([ - ('date', '<=', self.last_sync or fields.Date().today()), - ('online_transaction_identifier', '!=', False), - ('journal_id', 'in', self.journal_ids.ids), - ('online_account_id', '=', self.id) - ], order="date desc", limit=1) - transactions = [] - - start_date = date or last_stmt_line.date or self.last_sync - data = { - # If we are in a new sync, we do not give a start date; We will fetch as much as possible. Otherwise, the last sync is the start date. - 'start_date': start_date and format_date(self.env, start_date, date_format='yyyy-MM-dd'), - 'account_id': self.online_identifier, - 'last_transaction_identifier': last_stmt_line.online_transaction_identifier if not include_pendings else None, - 'currency_code': self.currency_id.name or self.journal_ids[0].currency_id.name or self.company_id.currency_id.name, - 'include_pendings': include_pendings, - 'include_foreign_currency': True, - } - pendings = [] - while True: - # While this is kind of a bad practice to do, it can happen that provider_data/account_data change between - # 2 calls, the reason is that those field contains the encrypted information needed to access the provider - # and first call can result in an error due to the encrypted token inside provider_data being expired for example. - # In such a case, we renew the token with the provider and send back the newly encrypted token inside provider_data - # which result in the information having changed, henceforth why those fields are passed at every loop. - data.update({ - 'provider_data': self.account_online_link_id.provider_data, - 'account_data': self.account_data, - }) - resp_json = self.account_online_link_id._fetch_odoo_fin('/proxy/v1/transactions', data=data) - if resp_json.get('balance'): - sign = -1 if self.inverse_balance_sign else 1 - self.balance = sign * resp_json['balance'] - if resp_json.get('account_data'): - self.account_data = resp_json['account_data'] - transactions += resp_json.get('transactions', []) - pendings += resp_json.get('pendings', []) - if not resp_json.get('next_data'): - break - data['next_data'] = resp_json.get('next_data') or {} - - return { - 'transactions': self._format_transactions(transactions), - 'pendings': self._format_transactions(pendings), - } - - def get_formatted_balances(self): - balances = {} - for account in self: - if account.currency_id: - formatted_balance = format_amount(self.env, account.balance, account.currency_id) - else: - formatted_balance = '%.2f' % account.balance - balances[account.id] = [formatted_balance, account.balance] - return balances - - ########### - # HELPERS # - ########### - - def _get_filtered_transactions(self, new_transactions): - """ This function will filter transaction to avoid duplicate transactions. - To do that, we're comparing the received online_transaction_identifier with - those in the database. If there is a match, the new transaction is ignored. - """ - self.ensure_one() - - journal_id = self.journal_ids[0] - existing_bank_statement_lines = self.env['account.bank.statement.line'].search_fetch( - [ - ('journal_id', '=', journal_id.id), - ('online_transaction_identifier', 'in', [ - transaction['online_transaction_identifier'] - for transaction in new_transactions - if transaction.get('online_transaction_identifier') - ]), - ], - ['online_transaction_identifier'] - ) - existing_online_transaction_identifier = set(existing_bank_statement_lines.mapped('online_transaction_identifier')) - - filtered_transactions = [] - # Remove transactions already imported in Odoo - for transaction in new_transactions: - if transaction_identifier := transaction['online_transaction_identifier']: - if transaction_identifier in existing_online_transaction_identifier: - continue - existing_online_transaction_identifier.add(transaction_identifier) - - filtered_transactions.append(transaction) - return filtered_transactions - - def _format_transactions(self, new_transactions): - """ This function format transactions: - It will: - - Replace the foreign currency code with the corresponding currency id and activating the currencies that are not active - - Change inverse the transaction sign if the setting is activated - - Parsing the date - - Setting the account online account and the account journal - """ - self.ensure_one() - transaction_sign = -1 if self.inverse_transaction_sign else 1 - currencies = self.env['res.currency'].with_context(active_test=False).search([]) - currency_code_mapping = {currency.name: currency for currency in currencies} - - formatted_transactions = [] - for transaction in new_transactions: - if transaction.get('foreign_currency_code'): - currency = currency_code_mapping.get(transaction.pop('foreign_currency_code')) - if currency: - transaction.update({'foreign_currency_id': currency.id}) - if not currency.active: - currency.active = True - - formatted_transactions.append({ - **transaction, - 'amount': transaction['amount'] * transaction_sign, - 'date': fields.Date.from_string(transaction['date']), - 'online_account_id': self.id, - 'journal_id': self.journal_ids[0].id, - 'company_id': self.company_id.id, - }) - return formatted_transactions - - def action_reset_fetching_status(self): - """ - This action will reset the fetching status to avoid the problem when there is an error during the - synchronisation that would block the customer with his connection since we block the fetch due that value. - With this he has a button that can reset the fetching status. - """ - self.fetching_status = None - - -class AccountOnlineLink(models.Model): - _name = 'account.online.link' - _description = 'Bank Connection' - _inherit = ['mail.thread', 'mail.activity.mixin'] - - def _compute_next_synchronization(self): - for rec in self: - rec.next_refresh = self.env['ir.cron'].sudo().search([('id', '=', self.env.ref('odex30_account_online_sync.online_sync_cron').id)], limit=1).nextcall - - account_online_account_ids = fields.One2many('account.online.account', 'account_online_link_id') - last_refresh = fields.Datetime(readonly=True, default=fields.Datetime.now) - next_refresh = fields.Datetime("Next synchronization", compute='_compute_next_synchronization') - state = fields.Selection([('connected', 'Connected'), ('error', 'Error'), ('disconnected', 'Not Connected')], - default='disconnected', tracking=True, required=True, readonly=True) - connection_state_details = fields.Json() - auto_sync = fields.Boolean( - default=True, - string="Automatic synchronization", - help="""If possible, we will try to automatically fetch new transactions for this record - \nIf the automatic sync is disabled. that will be due to security policy on the bank's end. So, they have to launch the sync manually""", - ) - company_id = fields.Many2one('res.company', required=True, default=lambda self: self.env.company) - has_unlinked_accounts = fields.Boolean(default=True, help="True if that connection still has accounts that are not linked to an Odoo journal") - show_sync_actions = fields.Boolean(compute='_compute_show_sync_actions') - - # Information received from OdooFin, should not be tampered with - name = fields.Char(help="Institution Name", readonly=True) - client_id = fields.Char(help="Represent a link for a given user towards a banking institution", readonly=True) - refresh_token = fields.Char(help="Token used to sign API request, Never disclose it", - readonly=True, groups="base.group_system") - access_token = fields.Char(help="Token used to access API.", readonly=True, groups="account.group_account_basic") - provider_data = fields.Char(help="Information needed to interact with third party provider", readonly=True) - expiring_synchronization_date = fields.Date(help="Date when the consent for this connection expires", - readonly=True) - journal_ids = fields.One2many('account.journal', compute='_compute_journal_ids') - provider_type = fields.Char(help="Third Party Provider", readonly=True) - - ################### - # Compute methods # - ################### - - @api.depends('account_online_account_ids') - def _compute_journal_ids(self): - for online_link in self: - online_link.journal_ids = online_link.account_online_account_ids.journal_ids - - @api.depends('company_id') - @api.depends_context('allowed_company_ids') - def _compute_show_sync_actions(self): - for online_link in self: - online_link.show_sync_actions = online_link.company_id in self.env.companies - - ########################## - # Wizard opening actions # - ########################## - def create_new_bank_account_action(self, data=None): - self.ensure_one() - # We do return the bank account setup wizard if we don't have minimum info - if not data or not data.get('account_number'): - ctx = self.env.context - # if this was called from kanban box, active_model is in context - if ctx.get('active_model') == 'account.journal': - ctx = {**ctx, 'default_linked_journal_id': ctx.get('active_id', False), 'dialog_size': 'medium'} - return { - 'type': 'ir.actions.act_window', - 'name': _('Setup Bank Account'), - 'res_model': 'account.setup.bank.manual.config', - 'target': 'new', - 'view_mode': 'form', - 'context': ctx, - 'views': [(False, 'form')], - } - - bank = self.env['res.bank'] - if data.get('name'): - bank = self.env['res.bank'].sudo().create({ - 'name': data['name'], - 'bic': data.get('swift_code'), - }) - - bank_account = self.env['res.partner.bank'].sudo().create({ - 'acc_number': data.get('account_number'), - 'bank_id': bank.id, - 'partner_id': self.company_id.partner_id.id, - }) - - self.env['account.journal'].sudo().create({ - 'name': data.get('account_number'), - 'type': data.get('journal_type') or 'bank', - 'bank_account_id': bank_account.id, - }) - - return {'type': 'ir.actions.client', 'tag': 'soft_reload'} - - def _link_accounts_to_journals_action(self, swift_code): - """ - This method opens a wizard allowing the user to link - his bank accounts with new or existing journal. - :return: An action openning a wizard to link bank accounts with account journal. - """ - self.ensure_one() - account_bank_selection_wizard = self.env['account.bank.selection'].create({ - 'account_online_link_id': self.id, - }) - - return { - "name": _("Select a Bank Account"), - "type": "ir.actions.act_window", - "res_model": "account.bank.selection", - "views": [[False, "form"]], - "target": "new", - "res_id": account_bank_selection_wizard.id, - 'context': dict(self.env.context, swift_code=swift_code), - } - - @api.model - def _show_fetched_transactions_action(self, stmt_line_ids, duplicates_from_date): - return self.env['account.bank.statement.line']._action_open_bank_reconciliation_widget( - extra_domain=[('id', 'in', stmt_line_ids.ids)], - name=_('Fetched Transactions'), - **({'default_context': {'duplicates_from_date': duplicates_from_date}} if duplicates_from_date else {}), - ) - - def _get_connection_state_details(self, journal): - self.ensure_one() - if self.connection_state_details and self.connection_state_details.get(str(journal.id)): - # We have to check that we have a key and a right value for this journal - # Because if we have an empty dict, the JS part will handle it as a Proxy object. - # To avoid that, we checked if we have a key in the dict and if the value is truthy. - return self.connection_state_details[str(journal.id)] - return None - - def _pop_connection_state_details(self, journal): - self.ensure_one() - if journal_connection_state_details := self._get_connection_state_details(journal): - self._set_connection_state_details(journal, {}) - return journal_connection_state_details - return None - - def _set_connection_state_details(self, journal, connection_state_details): - self.ensure_one() - existing_connection_state_details = self.connection_state_details or {} - self.connection_state_details = { - **existing_connection_state_details, - str(journal.id): connection_state_details, - } - - def _notify_connection_update(self, journal, connection_state_details): - """ The aim of this function is saving the last connection state details - (like if the status is success or in error) on the account.online.link - object. At the same moment, we're sending a websocket message to - accounting dashboard where we return the status of the connection. - To make sure that we don't return sensitive information, we filtered - the connection state details to only send by websocket information - like the connection status, how many transactions we fetched, and - the error type. In case of an error, the function is calling rollback - on the cursor and is committing the save on the account online link. - It's also usefull to commit in case of error to send the websocket message. - The commit is only called if we aren't in test mode and if the connection is - in error. - - :param journal: The journal for which we want to save the connection state details. - :param connection_state_details: The information about the status of the connection (like how many transactions fetched, ...) - """ - self.ensure_one() - - connection_state_details_status = connection_state_details['status'] # We're always waiting for a status in the dict. - if connection_state_details_status == 'error': - # In case the connection status is in error, we roll back everything before saving the status. - self.env.cr.rollback() - if not (connection_state_details_status == 'success' and connection_state_details.get('nb_fetched_transactions', 0) == 0): - self._set_connection_state_details( - journal=journal, - connection_state_details=connection_state_details, - ) - self.env.ref('account.group_account_user').users._bus_send( - 'online_sync', - { - 'id': journal.id, - 'connection_state_details': { - key: value - for key, value in connection_state_details.items() - if key in ('status', 'error_type', 'nb_fetched_transactions') - }, - }, - ) - if connection_state_details_status == 'error' and not tools.config['test_enable'] and not modules.module.current_test: - # In case the status is in error, and we aren't in test mode, we commit to save the last connection state and to send the websocket message - self.env.cr.commit() - - def _handle_odoofin_redirect_exception(self, mode='link'): - if mode == 'link': - return self.with_context({'redirect_reconnection': True}).action_new_synchronization() - return self.with_context({'redirect_reconnection': True})._open_iframe(mode=mode) - - ####################################################### - # Generic methods to contact server and handle errors # - ####################################################### - - @api.model - def _get_odoofin_url(self, url): - proxy_mode = self.env['ir.config_parameter'].sudo().get_param('odex30_account_online_sync.proxy_mode') or 'production' - if not pattern.match(proxy_mode) and not runbot_pattern.match(proxy_mode): - raise UserError(_('Invalid value for proxy_mode config parameter.')) - endpoint_url = 'https://%s.odoofin.com%s' % (proxy_mode, url) - if runbot_pattern.match(proxy_mode): - endpoint_url = '%s%s' % (proxy_mode, url) - return endpoint_url - - def _fetch_odoo_fin(self, url, data=None, ignore_status=False): - """ - Method used to fetch data from the Odoo Fin proxy. - :param url: Proxy's URL end point. - :param data: HTTP data request. - :return: A dict containing all data. - """ - if not data: - data = {} - if self.state == 'disconnected' and not ignore_status: - raise UserError(_('Please reconnect your online account.')) - if not url.startswith('/'): - raise UserError(_('Invalid URL')) - - timeout = int(self.env['ir.config_parameter'].sudo().get_param('odex30_account_online_sync.request_timeout')) or 60 - endpoint_url = self._get_odoofin_url(url) - cron = self.env.context.get('cron', False) - data['utils'] = { - 'request_timeout': timeout, - 'lang': get_lang(self.env).code, - 'server_version': odoo.release.serie, - 'db_uuid': self.env['ir.config_parameter'].sudo().get_param('database.uuid'), - 'cron': cron, - } - if request: - # many banking institutions require the end-user IP/user_agent for traceability - # of client-initiated actions. It won't be stored on odoofin side. - data['utils']['psu_info'] = { - 'ip': request.httprequest.remote_addr, - 'user_agent': request.httprequest.user_agent.string, - } - - try: - # We have to use sudo to pass record as some fields are protected from read for common users. - resp = requests.post(url=endpoint_url, json=data, timeout=timeout, auth=OdooFinAuth(record=self.sudo())) - resp_json = resp.json() - return self._handle_response(resp_json, url, data, ignore_status) - except (Timeout, ConnectionError, RequestException, ValueError): - _logger.warning('synchronization error') - raise UserError( - _("The online synchronization service is not available at the moment. " - "Please try again later.")) - - def _handle_response(self, resp_json, url, data, ignore_status=False): - # Response is a json-rpc response, therefore data is encapsulated inside error in case of error - # and inside result in case of success. - if not resp_json.get('error'): - result = resp_json.get('result') - state = result.get('odoofin_state') or False - message = result.get('display_message') or False - subject = message and _('Message') or False - self._log_information(state=state, message=message, subject=subject) - if result.get('provider_data'): - # Provider_data is extremely important and must be saved as soon as we received it - # as it contains encrypted credentials from external provider and if we loose them we - # loose access to the bank account, As it is possible that provider_data - # are received during a transaction containing multiple calls to the proxy, we ensure - # that provider_data is committed in database as soon as we received it. - self.provider_data = result.get('provider_data') - self.env.cr.commit() - return result - else: - error = resp_json.get('error') - # Not considered as error - if error.get('code') == 101: # access token expired, not an error - self._get_access_token() - return self._fetch_odoo_fin(url, data, ignore_status) - elif error.get('code') == 102: # refresh token expired, not an error - self._get_refresh_token() - self._get_access_token() - # We need to commit here because if we got a new refresh token, and a new access token - # It means that the token is active on the proxy and any further call resulting in an - # error would lose the new refresh_token hence blocking the account ad vitam eternam - self.env.cr.commit() - if self.journal_ids: # We can't do it unless we already have a journal - self._update_connection_status() - return self._fetch_odoo_fin(url, data, ignore_status) - elif error.get('code') == 300: # redirect, not an error - raise OdooFinRedirectException(mode=error.get('data', {}).get('mode', 'link')) - # If we are in the process of deleting the record ignore code 100 (invalid signature), 104 (account deleted) - # 106 (provider_data corrupted) and allow user to delete his record from this side. - elif error.get('code') in (100, 104, 106) and self.env.context.get('delete_sync'): - return {'delete': True} - # Log and raise error - error_details = error.get('data') - subject = error.get('message') - message = error_details.get('message') - state = error_details.get('odoofin_state') or 'error' - ctx = self.env.context.copy() - ctx['error_reference'] = error_details.get('error_reference') - ctx['provider_type'] = error_details.get('provider_type') - ctx['redirect_warning_url'] = error_details.get('redirect_warning_url') - - self.with_context(ctx)._log_information(state=state, subject=subject, message=message, reset_tx=True) - - def _log_information(self, state, subject=None, message=None, reset_tx=False): - # If the reset_tx flag is passed, it means that we have an error, and we want to log it on the record - # and then raise the error to the end user. To do that we first roll back the current transaction, - # then we write the error on the record, we commit those changes, and finally we raise the error. - if reset_tx: - self.env.cr.rollback() - try: - # if state is disconnected, and new state is error: ignore it - if state == 'error' and self.state == 'disconnected': - state = 'disconnected' - if state and self.state != state: - self.write({'state': state}) - if state in ('error', 'disconnected'): - self.account_online_account_ids.fetching_status = 'done' - if reset_tx: - context = self.env.context - button_label = url = None - if subject and message: - message_post = message - error_reference = context.get('error_reference') - provider = context.get('provider_type') - odoo_help_description = f'''ClientID: {self.client_id}\nInstitution: {self.name}\nError Reference: {error_reference}\nError Message: {message_post}\n''' - odoo_help_summary = f'Bank sync error ref: {error_reference} - Provider: {provider} - Client ID: {self.client_id}' - if context.get('redirect_warning_url'): - if context['redirect_warning_url'] == 'odoo_support': - url_params = urllib.parse.urlencode({'stage': 'bank_sync', 'summary': odoo_help_summary, 'description': odoo_help_description[:1500]}) - url = f'https://www.odoo.com/help?{url_params}' - message += _("\n\nIf you've already opened a ticket for this issue, don't report it again: a support agent will contact you shortly.") - message_post = Markup('%s
%s %s') % (message, _("You can contact Odoo support"), url, _("Here")) - button_label = _('Report issue') - else: - url = "https://www.odoo.com/documentation/18.0/applications/finance/accounting/bank/bank_synchronization.html#faq" - message_post = Markup('%s
%s %s') % (message_post, _("Check the documentation"), url, _("Here")) - button_label = _('Check the documentation') - self.message_post(body=message_post, subject=subject) - # In case of reset_tx, we commit the changes in order to have the message post saved - self.env.cr.commit() - # and then raise either a redirectWarning error so that customer can easily open an issue with Odoo, - # or eventually bring the user to the documentation if there's no need to contact the support. - if url: - action_id = { - "type": "ir.actions.act_url", - "url": url, - } - raise RedirectWarning(message, action_id, button_label) #pylint: disable=E0601 - # either a userError if there's no need to bother the support, or link to the doc. - raise UserError(message) - except (CacheMiss, MissingError): - # This exception can happen if record was created and rollbacked due to error in same transaction - # Therefore it is not possible to log information on it, in this case we just ignore it. - pass - - ############### - # API methods # - ############### - - def _get_access_token(self): - for link in self: - resp_json = link._fetch_odoo_fin('/proxy/v1/get_access_token', ignore_status=True) - link.access_token = resp_json.get('access_token', False) - - def _get_refresh_token(self): - # Use sudo as refresh_token field is not accessible to most user - for link in self.sudo(): - resp_json = link._fetch_odoo_fin('/proxy/v1/renew_token', ignore_status=True) - link.refresh_token = resp_json.get('refresh_token', False) - - def unlink(self): - to_unlink = self.env['account.online.link'] - for link in self: - try: - resp_json = link.with_context(delete_sync=True)._fetch_odoo_fin('/proxy/v1/delete_user', data={'provider_data': link.provider_data}, ignore_status=True) # delete proxy user - if resp_json.get('delete', True) is True: - to_unlink += link - except OdooFinRedirectException: - # Can happen that this call returns a redirect in mode link, in which case we delete the record - to_unlink += link - continue - except (UserError, RedirectWarning): - to_unlink += link - continue - result = super(AccountOnlineLink, to_unlink).unlink() - self.env['account.journal']._toggle_asynchronous_fetching_cron() - return result - - def _fetch_accounts(self, online_identifier=False): - self.ensure_one() - if online_identifier: - matching_account = self.account_online_account_ids.filtered(lambda l: l.online_identifier == online_identifier) - # Ignore account that is already there and linked to a journal as there is no need to fetch information for that one - if matching_account and matching_account.journal_ids: - return matching_account - # If we have the account locally but didn't link it to a journal yet, delete it first. - # This way, we'll get the information back from the proxy with updated balances. Avoiding potential issues. - elif matching_account and not matching_account.journal_ids: - matching_account.unlink() - accounts = {} - data = { - 'currency_code': self.company_id.currency_id.name, - } - swift_code = False - while True: - # While this is kind of a bad practice to do, it can happen that provider_data changes between - # 2 calls, the reason is that that field contains the encrypted information needed to access the provider - # and first call can result in an error due to the encrypted token inside provider_data being expired for example. - # In such a case, we renew the token with the provider and send back the newly encrypted token inside provider_data - # which result in the information having changed, henceforth why that field is passed at every loop. - data['provider_data'] = self.provider_data - # Retrieve information about a specific account - if online_identifier: - data['online_identifier'] = online_identifier - - resp_json = self._fetch_odoo_fin('/proxy/v1/accounts', data) - for acc in resp_json.get('accounts', []): - acc['account_online_link_id'] = self.id - currency_id = self.env['res.currency'].with_context(active_test=False).search([('name', '=', acc.pop('currency_code', ''))], limit=1) - if currency_id: - if not currency_id.active: - currency_id.sudo().active = True - acc['currency_id'] = currency_id.id - accounts[str(acc.get('online_identifier'))] = acc - swift_code = resp_json.get('swift_code') - if not resp_json.get('next_data'): - break - data['next_data'] = resp_json.get('next_data') - - if accounts: - self.has_unlinked_accounts = True - return self.env['account.online.account'].create(accounts.values()), swift_code - return False, False - - def _pre_check_fetch_transactions(self): - self.ensure_one() - # 'limit_time_real_cron' and 'limit_time_real' default respectively to -1 and 120. - # Manual fallbacks applied for non-POSIX systems where this key is disabled (set to None). - limit_time = tools.config['limit_time_real_cron'] or -1 - if limit_time <= 0: - limit_time = tools.config['limit_time_real'] or 120 - limit_time += 20 # Add 20 seconds to be sure that the process will have been killed - # if any account is actually creating entries and last_refresh was made less than cron_limit_time ago, skip fetching - if (self.account_online_account_ids.filtered(lambda account: account.fetching_status == 'processing') and - self.last_refresh + relativedelta(seconds=limit_time) > fields.Datetime.now()): - return False - # If not in the process of importing and auto_sync is not set, skip fetching - if (self.env.context.get('cron') and - not self.auto_sync and - not self.account_online_account_ids.filtered(lambda acc: acc.fetching_status in ('planned', 'waiting', 'processing'))): - return False - return True - - def _fetch_transactions(self, refresh=True, accounts=False, check_duplicates=False): - self.ensure_one() - # return early if condition to fetch transactions are not met - if not self._pre_check_fetch_transactions(): - return - - is_cron_running = self.env.context.get('cron') - acc = (accounts or self.account_online_account_ids).filtered('journal_ids') - self.last_refresh = fields.Datetime.now() - try: - # When manually fetching, refresh must still be done in case a redirect occurs - # however since transactions are always fetched inside a cron, in case we are manually - # fetching, trigger the cron and redirect customer to accounting dashboard - accounts_to_synchronize = acc - if not is_cron_running: - accounts_not_to_synchronize = self.env['account.online.account'] - account_to_reauth = False - for online_account in acc: - # Only get transactions on account linked to a journal - if refresh and online_account.fetching_status not in ('planned', 'processing'): - refresh_res = online_account._refresh() - if not refresh_res['success']: - if refresh_res['data'].get('mode') == 'updateCredentials': - account_to_reauth = online_account - accounts_not_to_synchronize += online_account - continue - online_account.fetching_status = 'waiting' - if account_to_reauth: - return self._open_iframe( - mode='updateCredentials', - include_param={ - 'account_online_identifier': account_to_reauth.online_identifier, - }, - ) - accounts_to_synchronize = acc - accounts_not_to_synchronize - if not accounts_to_synchronize: - return - - def get_duplicates_from_date(statement_lines, journal): - if check_duplicates and statement_lines: - from_date = fields.Date.to_string(statement_lines.sorted('date')[0].date) - if journal._has_duplicate_transactions(from_date): - return from_date - - for online_account in accounts_to_synchronize: - journal = online_account.journal_ids[0] - online_account.fetching_status = 'processing' - # Committing here so that multiple thread calling this method won't execute in parallel and import duplicates transaction - self.env.cr.commit() - try: - transactions = online_account._retrieve_transactions().get('transactions', []) - except RedirectWarning as redirect_warning: - self._notify_connection_update( - journal=journal, - connection_state_details={ - 'status': 'error', - 'error_type': 'redirect_warning', - 'error_message': redirect_warning.args[0], - 'action': redirect_warning.args[1], - }, - ) - raise - except OdooFinRedirectException as redirect_exception: - self._notify_connection_update( - journal=journal, - connection_state_details={ - 'status': 'error', - 'error_type': 'odoofin_redirect', - 'action': self._handle_odoofin_redirect_exception(mode=redirect_exception.mode), - }, - ) - raise - - sorted_transactions = sorted(transactions, key=lambda transaction: transaction['date']) - if not is_cron_running: - # we want to import the first 100 transaction, show them to the user - # and import the rest asynchronously with the 'online_sync_cron_waiting_synchronization' cron - total = sum([transaction['amount'] for transaction in transactions]) - statement_lines = self.env['account.bank.statement.line'].with_context(transactions_total=total)._online_sync_bank_statement(sorted_transactions[:100], online_account) - online_account.fetching_status = 'planned' if len(transactions) > 100 else 'done' - domain = None - if statement_lines: - domain = [('id', 'in', statement_lines.ids)] - - duplicates_from_date = get_duplicates_from_date(statement_lines, journal) - return self.env['account.bank.statement.line']._action_open_bank_reconciliation_widget( - extra_domain=domain, - name=_('Fetched Transactions'), - default_context={**self.env.context, 'default_journal_id': journal.id, 'duplicates_from_date': duplicates_from_date}, - ) - else: - statement_lines = self.env['account.bank.statement.line']._online_sync_bank_statement(sorted_transactions, online_account) - online_account.fetching_status = 'done' - duplicates_from_date = get_duplicates_from_date(statement_lines, journal) - self._notify_connection_update( - journal=journal, - connection_state_details={ - 'status': 'success', - 'nb_fetched_transactions': len(statement_lines), - 'action': self._show_fetched_transactions_action(statement_lines, duplicates_from_date), - }, - ) - return - except OdooFinRedirectException as e: - return self._handle_odoofin_redirect_exception(mode=e.mode) - - def _get_consent_expiring_date(self, data=None): - self.ensure_one() - if not data: # Small hack to avoid breaking the stable policy - data = self._fetch_odoo_fin('/proxy/v1/consent_expiring_date', ignore_status=True) - - if data.get('consent_expiring_date'): - expiring_synchronization_date = fields.Date.to_date(data['consent_expiring_date']) - if expiring_synchronization_date != self.expiring_synchronization_date: - # TDE TODO: master: use generic activity mixin methods instead - bank_sync_activity_type_id = self.env.ref('odex30_account_online_sync.bank_sync_activity_update_consent') - account_journal_model_id = self.env['ir.model']._get_id('account.journal') - - # Remove old activities - self.env['mail.activity'].search([ - ('res_id', 'in', self.journal_ids.ids), - ('res_model_id', '=', account_journal_model_id), - ('activity_type_id', '=', bank_sync_activity_type_id.id), - ('date_deadline', '<=', self.expiring_synchronization_date), - ('user_id', '=', self.env.user.id), - ]).unlink() - - # Create a new activity for each journals for this synch - self.expiring_synchronization_date = expiring_synchronization_date - new_activity_vals = [] - for journal in self.journal_ids: - new_activity_vals.append({ - 'res_id': journal.id, - 'res_model_id': account_journal_model_id, - 'date_deadline': self.expiring_synchronization_date, - 'summary': _("Bank Synchronization: Update your consent"), - 'note': data.get('activity_message') or '', - 'activity_type_id': bank_sync_activity_type_id.id, - }) - self.env['mail.activity'].create(new_activity_vals) - elif self.expiring_synchronization_date and self.expiring_synchronization_date < fields.Date.context_today(self): - # Avoid an infinite "expired synchro" if the provider - # doesn't send us a new consent expiring date - self.expiring_synchronization_date = None - - def _update_connection_status(self): - self.ensure_one() - resp_json = self._fetch_odoo_fin('/proxy/v2/connection_status', ignore_status=True) - - self._get_consent_expiring_date(resp_json) - - # Returning what we receive from Odoo Fin to allow function extension - return resp_json - - def _authorize_access(self, data_access_token): - """ - This method is used to allow an existing connection to give temporary access - to a new connection in order to see the list of available unlinked accounts. - We pass as parameter the list of already linked account, so that if there are - no more accounts to link, we will receive a response telling us so and we won't - call authorize for that connection later on. - """ - self.ensure_one() - data = { - 'linked_accounts': self.account_online_account_ids.filtered('journal_ids').mapped('online_identifier'), - 'record_access_token': data_access_token, - } - try: - resp_json = self._fetch_odoo_fin('/proxy/v1/authorize_access', data) - self.has_unlinked_accounts = resp_json.get('has_unlinked_accounts') - except UserError: - # We don't want to throw an error to the customer so ignore error - pass - - @api.model - def _cron_delete_unused_connection(self): - account_online_links = self.search([ - ('write_date', '<=', fields.Datetime.now() - relativedelta(months=1)), - ]) - for link in account_online_links: - if not link.account_online_account_ids.filtered('journal_ids'): - link.unlink() - - @api.returns('mail.message', lambda value: value.id) - def message_post(self, **kwargs): - """Override to log all message to the linked journal as well.""" - for journal in self.journal_ids: - journal.message_post(**kwargs) - return super(AccountOnlineLink, self).message_post(**kwargs) - - ################################ - # Callback methods from iframe # - ################################ - - def success(self, mode, data): - if data: - self.write(data) - # Provider_data is extremely important and must be saved as soon as we received it - # as it contains encrypted credentials from external provider and if we loose them we - # loose access to the bank account, As it is possible that provider_data - # are received during a transaction containing multiple calls to the proxy, we ensure - # that provider_data is committed in database as soon as we received it. - if data.get('provider_data'): - self.env.cr.commit() - - self._update_connection_status() - # if for some reason we just have to update the record without doing anything else, the mode will be set to 'none' - if mode == 'none': - return {'type': 'ir.actions.client', 'tag': 'reload'} - try: - method_name = '_success_%s' % mode - method = getattr(self, method_name) - except AttributeError: - message = _("This version of Odoo appears to be outdated and does not support the '%s' sync mode. " - "Installing the latest update might solve this.", mode) - _logger.info('Online sync: %s' % (message,)) - self.env.cr.rollback() - self._log_information(state='error', subject=_('Internal Error'), message=message, reset_tx=True) - raise UserError(message) - action = method() - return action or self.env['ir.actions.act_window']._for_xml_id('account.open_account_journal_dashboard_kanban') - - @api.model - def connect_existing_account(self, data): - # extract client_id and online_identifier from data and retrieve the account detail from the connection. - # If we have a journal in context, assign to journal, otherwise create new journal then fetch transaction - client_id = data.get('client_id') - online_identifier = data.get('online_identifier') - if client_id and online_identifier: - online_link = self.search([('client_id', '=', client_id)], limit=1) - if not online_link: - return {'type': 'ir.actions.client', 'tag': 'reload'} - new_account, swift_code = online_link._fetch_accounts(online_identifier=online_identifier) - if new_account: - new_account._assign_journal(swift_code) - action = online_link._fetch_transactions(accounts=new_account, check_duplicates=True) - return action or self.env['ir.actions.act_window']._for_xml_id('account.open_account_journal_dashboard_kanban') - raise UserError(_("The consent for the selected account has expired.")) - return {'type': 'ir.actions.client', 'tag': 'reload'} - - def exchange_token(self, exchange_token): - self.ensure_one() - # Exchange token to retrieve client_id and refresh_token from proxy account - data = { - 'exchange_token': exchange_token, - 'company_id': self.env.company.id, - 'user_id': self.env.user.id - } - resp_json = self._fetch_odoo_fin('/proxy/v1/exchange_token', data=data, ignore_status=True) - # Write in sudo mode as those fields are protected from users - self.sudo().write({ - 'client_id': resp_json.get('client_id'), - 'refresh_token': resp_json.get('refresh_token'), - 'access_token': resp_json.get('access_token') - }) - return True - - def _success_link(self): - self.ensure_one() - self._log_information(state='connected') - account_online_accounts, swift_code = self._fetch_accounts() - if account_online_accounts and len(account_online_accounts) == 1: - account_online_accounts._assign_journal(swift_code) - return self._fetch_transactions(accounts=account_online_accounts, check_duplicates=True) - return self._link_accounts_to_journals_action(swift_code) - - def _success_updateCredentials(self): - self.ensure_one() - return self._fetch_transactions(refresh=False) - - def _success_refreshAccounts(self): - self.ensure_one() - return self._fetch_transactions(refresh=False) - - def _success_reconnect(self): - self.ensure_one() - self._log_information(state='connected') - return self._fetch_transactions(check_duplicates=True) - - ################## - # action buttons # - ################## - - def action_new_synchronization(self, preferred_inst=None, journal_id=False): - # Search for an existing link that was not fully connected - online_link = self - if not online_link or online_link.provider_data: - online_link = self.search([('account_online_account_ids', '=', False)], limit=1) - # If not found, create a new one - if not online_link or online_link.provider_data: - online_link = self.create({}) - return online_link._open_iframe('link', preferred_institution=preferred_inst, journal_id=journal_id) - - def action_update_credentials(self): - return self._open_iframe('updateCredentials') - - def action_fetch_transactions(self): - self.account_online_account_ids.fetching_status = None - action = self._fetch_transactions() - return action or self.env['ir.actions.act_window']._for_xml_id('account.open_account_journal_dashboard_kanban') - - def action_reconnect_account(self): - return self._open_iframe('reconnect') - - def _open_iframe(self, mode='link', include_param=None, preferred_institution=False, journal_id=False): - self.ensure_one() - if self.client_id and self.sudo().refresh_token: - try: - self._get_access_token() - except OdooFinRedirectException: - # Delete record and open iframe in a new one - self.unlink() - return self.create({})._open_iframe('link') - - proxy_mode = self.env['ir.config_parameter'].sudo().get_param('odex30_account_online_sync.proxy_mode') or 'production' - country = self.env['account.journal'].browse(journal_id).company_id.account_fiscal_country_id or self.env.company.country_id - action = { - 'type': 'ir.actions.client', - 'tag': 'odoo_fin_connector', - 'id': self.id, - 'params': { - 'proxyMode': proxy_mode, - 'clientId': self.client_id, - 'accessToken': self.access_token, - 'mode': mode, - 'includeParam': { - 'lang': get_lang(self.env).code, - 'countryCode': country.code, - 'countryName': country.display_name, - 'redirect_reconnection': self.env.context.get('redirect_reconnection'), - 'serverVersion': odoo.release.serie, - 'mfa_type': self.env.user._mfa_type(), - } - }, - 'context': { - 'dialog_size': 'medium', - }, - } - if self.provider_data: - action['params']['providerData'] = self.provider_data - if preferred_institution: - action['params']['includeParam']['clickedInstitution'] = preferred_institution - if journal_id: - action['context']['active_model'] = 'account.journal' - action['context']['active_id'] = journal_id - - if mode == 'link': - user_email = self.env.user.email or self.env.ref('base.user_admin').email or '' # Necessary for some providers onboarding - action['params']['includeParam']['dbUuid'] = self.env['ir.config_parameter'].sudo().get_param('database.uuid') - action['params']['includeParam']['userEmail'] = user_email - # Compute a hash of a random string for each connection in success - existing_link = self.search([('state', '!=', 'disconnected'), ('has_unlinked_accounts', '=', True)]) - if existing_link: - record_access_token = base64.b64encode(uuid.uuid4().bytes).decode('utf-8') - for link in existing_link: - link._authorize_access(record_access_token) - action['params']['includeParam']['recordAccessToken'] = record_access_token - - if include_param: - action['params']['includeParam'].update(include_param) - return action diff --git a/dev_odex30_accounting/odex30_account_online_sync/models/bank_rec_widget.py b/dev_odex30_accounting/odex30_account_online_sync/models/bank_rec_widget.py deleted file mode 100644 index 273c43f..0000000 --- a/dev_odex30_accounting/odex30_account_online_sync/models/bank_rec_widget.py +++ /dev/null @@ -1,16 +0,0 @@ -from odoo import models - - -class BankRecWidget(models.Model): - _inherit = 'bank.rec.widget' - - def _action_validate(self): - # EXTENDS account_accountant - super()._action_validate() - line = self.st_line_id - if line.partner_id and line.online_partner_information: - # write value for account and merchant on partner only if partner has no value, - # in case value are different write False - value_merchant = line.partner_id.online_partner_information or line.online_partner_information - value_merchant = value_merchant if value_merchant == line.online_partner_information else False - line.partner_id.online_partner_information = value_merchant diff --git a/dev_odex30_accounting/odex30_account_online_sync/models/company.py b/dev_odex30_accounting/odex30_account_online_sync/models/company.py deleted file mode 100644 index 84feee2..0000000 --- a/dev_odex30_accounting/odex30_account_online_sync/models/company.py +++ /dev/null @@ -1,15 +0,0 @@ -# -*- coding: utf-8 -*- - -from odoo import api, models - - -class ResCompany(models.Model): - _inherit = "res.company" - - @api.model - def setting_init_bank_account_action(self): - """ - Override the "setting_init_bank_account_action" in accounting menu - and change the flow for the "Add a bank account" menu item in dashboard. - """ - return self.env['account.online.link'].action_new_synchronization() diff --git a/dev_odex30_accounting/odex30_account_online_sync/models/mail_activity_type.py b/dev_odex30_accounting/odex30_account_online_sync/models/mail_activity_type.py deleted file mode 100644 index 93e61bf..0000000 --- a/dev_odex30_accounting/odex30_account_online_sync/models/mail_activity_type.py +++ /dev/null @@ -1,14 +0,0 @@ -from odoo import api, models - - -class MailActivityType(models.Model): - _inherit = "mail.activity.type" - - @api.model - def _get_model_info_by_xmlid(self): - info = super()._get_model_info_by_xmlid() - info['odex30_account_online_sync.bank_sync_activity_update_consent'] = { - 'res_model': 'account.journal', - 'unlink': False, - } - return info diff --git a/dev_odex30_accounting/odex30_account_online_sync/models/odoofin_auth.py b/dev_odex30_accounting/odex30_account_online_sync/models/odoofin_auth.py deleted file mode 100644 index 19288f6..0000000 --- a/dev_odex30_accounting/odex30_account_online_sync/models/odoofin_auth.py +++ /dev/null @@ -1,46 +0,0 @@ -import base64 -import hashlib -import hmac -import json -import requests -import time -import werkzeug.urls - - -class OdooFinAuth(requests.auth.AuthBase): - - def __init__(self, record=None): - self.access_token = record and record.access_token or False - self.refresh_token = record and record.refresh_token or False - self.client_id = record and record.client_id or False - - def __call__(self, request): - # We don't sign request that still don't have a client_id/refresh_token - if not self.client_id or not self.refresh_token: - return request - # craft the message (timestamp|url path|client_id|access_token|query params|body content) - msg_timestamp = int(time.time()) - parsed_url = werkzeug.urls.url_parse(request.path_url) - - body = request.body - if isinstance(body, bytes): - body = body.decode('utf-8') - body = json.loads(body) - - message = '%s|%s|%s|%s|%s|%s' % ( - msg_timestamp, # timestamp - parsed_url.path, # url path - self.client_id, - self.access_token, - json.dumps(werkzeug.urls.url_decode(parsed_url.query), sort_keys=True), # url query params sorted by key - json.dumps(body, sort_keys=True)) # http request body - - h = hmac.new(base64.b64decode(self.refresh_token), message.encode('utf-8'), digestmod=hashlib.sha256) - - request.headers.update({ - 'odoofin-client-id': self.client_id, - 'odoofin-access-token': self.access_token, - 'odoofin-signature': base64.b64encode(h.digest()), - 'odoofin-timestamp': msg_timestamp, - }) - return request diff --git a/dev_odex30_accounting/odex30_account_online_sync/models/partner.py b/dev_odex30_accounting/odex30_account_online_sync/models/partner.py deleted file mode 100644 index 7cb6bc9..0000000 --- a/dev_odex30_accounting/odex30_account_online_sync/models/partner.py +++ /dev/null @@ -1,7 +0,0 @@ -from odoo import models, fields - - -class ResPartner(models.Model): - _inherit = 'res.partner' - - online_partner_information = fields.Char(readonly=True) diff --git a/dev_odex30_accounting/odex30_account_online_sync/security/account_online_sync_security.xml b/dev_odex30_accounting/odex30_account_online_sync/security/account_online_sync_security.xml deleted file mode 100644 index 9a4c704..0000000 --- a/dev_odex30_accounting/odex30_account_online_sync/security/account_online_sync_security.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - Account online link company rule - - - [('company_id', 'parent_of', company_ids)] - - - Online account company rule - - - [('account_online_link_id.company_id','parent_of', company_ids)] - - diff --git a/dev_odex30_accounting/odex30_account_online_sync/security/ir.model.access.csv b/dev_odex30_accounting/odex30_account_online_sync/security/ir.model.access.csv deleted file mode 100644 index adca78d..0000000 --- a/dev_odex30_accounting/odex30_account_online_sync/security/ir.model.access.csv +++ /dev/null @@ -1,12 +0,0 @@ -id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink -access_account_online_link_id,access_account_online_link_id,model_account_online_link,account.group_account_basic,1,1,1,0 -access_account_online_link_id_readonly,access_account_online_link_id_readonly,model_account_online_link,account.group_account_readonly,1,0,0,0 -access_account_online_link_id_manager,access_account_online_link_id manager,model_account_online_link,account.group_account_manager,1,1,1,1 -access_account_online_account_id,access_account_online_account_id,model_account_online_account,account.group_account_basic,1,1,1,0 -access_account_online_account_id_readonly,access_account_online_account_id_readonly,model_account_online_account,account.group_account_readonly,1,0,0,0 -access_account_online_account_id_manager,access_account_online_account_id manager,model_account_online_account,account.group_account_manager,1,1,1,1 -access_account_bank_selection_manager,access.account.bank.selection manager,model_account_bank_selection,account.group_account_manager,1,1,1,1 -access_account_bank_selection,access.account.bank.selection basic,model_account_bank_selection,account.group_account_basic,1,1,1,0 -access_account_bank_statement_line_transient,access_account_bank_statement_line_transient,model_account_bank_statement_line_transient,account.group_account_manager,1,1,1,1 -access_account_missing_transaction_wizard,access_account_missing_transaction_wizard,model_account_missing_transaction_wizard,account.group_account_manager,1,1,1,1 -access_account_duplicate_transaction_wizard,access_account_duplicate_transaction_wizard,model_account_duplicate_transaction_wizard,account.group_account_user,1,1,1,0 diff --git a/dev_odex30_accounting/odex30_account_online_sync/static/src/components/account_duplicate_transaction/account_duplicate_transaction_form.js b/dev_odex30_accounting/odex30_account_online_sync/static/src/components/account_duplicate_transaction/account_duplicate_transaction_form.js deleted file mode 100644 index 403bbe4..0000000 --- a/dev_odex30_accounting/odex30_account_online_sync/static/src/components/account_duplicate_transaction/account_duplicate_transaction_form.js +++ /dev/null @@ -1,37 +0,0 @@ -import { formView } from "@web/views/form/form_view"; -import { FormController } from "@web/views/form/form_controller"; -import { registry } from "@web/core/registry"; -import { useCheckDuplicateService } from "./account_duplicate_transaction_hook"; - -export class AccountDuplicateTransactionsFormController extends FormController { - setup() { - super.setup(); - this.duplicateCheckService = useCheckDuplicateService(); - } - - async beforeExecuteActionButton(clickParams) { - if (clickParams.name === "delete_selected_transactions") { - const selected = this.duplicateCheckService.selectedLines; - if (selected.size) { - await this.orm.call( - "account.bank.statement.line", - "unlink", - [Array.from(selected)], - ); - this.env.services.action.doAction({type: 'ir.actions.client', tag: 'reload'}); - } - return false; - } - return super.beforeExecuteActionButton(...arguments); - } - - get cogMenuProps() { - const props = super.cogMenuProps; - props.items.action = []; - return props; - } -} - -export const form = { ...formView, Controller: AccountDuplicateTransactionsFormController }; - -registry.category("views").add("account_duplicate_transactions_form", form); diff --git a/dev_odex30_accounting/odex30_account_online_sync/static/src/components/account_duplicate_transaction/account_duplicate_transaction_hook.js b/dev_odex30_accounting/odex30_account_online_sync/static/src/components/account_duplicate_transaction/account_duplicate_transaction_hook.js deleted file mode 100644 index 64b1604..0000000 --- a/dev_odex30_accounting/odex30_account_online_sync/static/src/components/account_duplicate_transaction/account_duplicate_transaction_hook.js +++ /dev/null @@ -1,6 +0,0 @@ -import { useService } from "@web/core/utils/hooks"; -import { useState } from "@odoo/owl"; - -export function useCheckDuplicateService() { - return useState(useService("odex30_account_online_sync.duplicate_check_service")); -} diff --git a/dev_odex30_accounting/odex30_account_online_sync/static/src/components/account_duplicate_transaction/account_duplicate_transaction_service.js b/dev_odex30_accounting/odex30_account_online_sync/static/src/components/account_duplicate_transaction/account_duplicate_transaction_service.js deleted file mode 100644 index b5571d2..0000000 --- a/dev_odex30_accounting/odex30_account_online_sync/static/src/components/account_duplicate_transaction/account_duplicate_transaction_service.js +++ /dev/null @@ -1,21 +0,0 @@ -import { registry } from "@web/core/registry"; - -class AccountDuplicateTransactionsServiceModel { - constructor() { - this.selectedLines = new Set(); - } - - updateLIne(selected, id) { - this.selectedLines[selected ? "add" : "delete"](id); - } -} - -const duplicateCheckService = { - start(env, services) { - return new AccountDuplicateTransactionsServiceModel(); - }, -}; - -registry - .category("services") - .add("odex30_account_online_sync.duplicate_check_service", duplicateCheckService); diff --git a/dev_odex30_accounting/odex30_account_online_sync/static/src/components/account_duplicate_transaction/account_duplicate_transactions_x2many.js b/dev_odex30_accounting/odex30_account_online_sync/static/src/components/account_duplicate_transaction/account_duplicate_transactions_x2many.js deleted file mode 100644 index d1d2a8e..0000000 --- a/dev_odex30_accounting/odex30_account_online_sync/static/src/components/account_duplicate_transaction/account_duplicate_transactions_x2many.js +++ /dev/null @@ -1,50 +0,0 @@ -import { onMounted } from "@odoo/owl"; -import { registry } from "@web/core/registry"; -import { ListRenderer } from "@web/views/list/list_renderer"; -import { X2ManyField, x2ManyField } from "@web/views/fields/x2many/x2many_field"; -import { useCheckDuplicateService } from "./account_duplicate_transaction_hook"; - -export class AccountDuplicateTransactionsListRenderer extends ListRenderer { - static template = "odex30_account_online_sync.AccountDuplicateTransactionsListRenderer"; - static recordRowTemplate = "odex30_account_online_sync.AccountDuplicateTransactionsRecordRow"; - - setup() { - super.setup(); - this.duplicateCheckService = useCheckDuplicateService(); - - onMounted(() => { - this.deleteButton = document.querySelector('button[name="delete_selected_transactions"]'); - this.deleteButton.disabled = true; - }); - } - - toggleRecordSelection(selected, record) { - this.duplicateCheckService.updateLIne(selected, record.data.id); - this.deleteButton.disabled = this.duplicateCheckService.selectedLines.size === 0; - } - - get hasSelectors() { - return true; - } - - getRowClass(record) { - let classes = super.getRowClass(record); - const firstIdsInGroup = this.env.model.root.data.first_ids_in_group; - if (firstIdsInGroup instanceof Array && firstIdsInGroup.includes(record.data.id)) { - classes += " account_duplicate_transactions_lines_list_x2many_group_line"; - } - return classes; - } -} - -export class AccountDuplicateTransactionsLinesListX2ManyField extends X2ManyField { - static components = { - ...X2ManyField.components, - ListRenderer: AccountDuplicateTransactionsListRenderer, - }; -} - -registry.category("fields").add("account_duplicate_transactions_lines_list_x2many", { - ...x2ManyField, - component: AccountDuplicateTransactionsLinesListX2ManyField, -}); diff --git a/dev_odex30_accounting/odex30_account_online_sync/static/src/components/account_duplicate_transaction/account_duplicate_transactions_x2many.scss b/dev_odex30_accounting/odex30_account_online_sync/static/src/components/account_duplicate_transaction/account_duplicate_transactions_x2many.scss deleted file mode 100644 index 4b147a5..0000000 --- a/dev_odex30_accounting/odex30_account_online_sync/static/src/components/account_duplicate_transaction/account_duplicate_transactions_x2many.scss +++ /dev/null @@ -1,3 +0,0 @@ -.account_duplicate_transactions_lines_list_x2many_group_line { - border-top-width: thick; -} diff --git a/dev_odex30_accounting/odex30_account_online_sync/static/src/components/account_duplicate_transaction/account_duplicate_transactions_x2many.xml b/dev_odex30_accounting/odex30_account_online_sync/static/src/components/account_duplicate_transaction/account_duplicate_transactions_x2many.xml deleted file mode 100644 index 52f66ff..0000000 --- a/dev_odex30_accounting/odex30_account_online_sync/static/src/components/account_duplicate_transaction/account_duplicate_transactions_x2many.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/dev_odex30_accounting/odex30_account_online_sync/static/src/components/bank_configure/bank_configure.js b/dev_odex30_accounting/odex30_account_online_sync/static/src/components/bank_configure/bank_configure.js deleted file mode 100644 index 0469a8d..0000000 --- a/dev_odex30_accounting/odex30_account_online_sync/static/src/components/bank_configure/bank_configure.js +++ /dev/null @@ -1,84 +0,0 @@ -/** @odoo-module */ - -import { registry } from "@web/core/registry"; -import { standardWidgetProps } from "@web/views/widgets/standard_widget_props"; -import { useService, useBus } from "@web/core/utils/hooks"; -import { SIZES } from "@web/core/ui/ui_service"; -import { Component, useState, useRef, onWillStart } from "@odoo/owl"; - -class BankConfigureWidget extends Component { - static template = "account.BankConfigureWidget"; - static props = { - ...standardWidgetProps, - } - setup() { - this.container = useRef("container"); - this.allInstitutions = []; - this.state = useState({ - isLoading: true, - institutions: [], - gridStyle: "grid-template-columns: repeat(5, minmax(90px, 1fr));" - }); - this.orm = useService("orm"); - this.action = useService("action"); - this.ui = useService("ui"); - onWillStart(this.fetchInstitutions); - useBus(this.ui.bus, "resize", this.computeGrid); - } - - computeGrid() { - if (this.allInstitutions.length > 4) { - let containerWidth = this.container.el ? this.container.el.offsetWidth - 32 : 0; - // when the container width can't be computed, use the screen size and number of journals. - if (!containerWidth) { - if (this.ui.size >= SIZES.XXL) { - containerWidth = window.innerWidth / (this.props.record.model.root.count < 6 ? 2 : 3); - } else { - containerWidth = Math.max(this.ui.size * 100, 400); - } - } - const canFit = Math.floor(containerWidth / 100); - const numberOfRows = (Math.floor((this.allInstitutions.length + 1) / 2) >= canFit) + 1; - this.state.gridStyle = `grid-template-columns: repeat(${canFit}, minmax(90px, 1fr)); - grid-template-rows: repeat(${numberOfRows}, 1fr); - grid-auto-rows: 0px; - `; - } - this.state.institutions = this.allInstitutions; - } - - async fetchInstitutions() { - this.orm.silent.call(this.props.record.resModel, "fetch_online_sync_favorite_institutions", [this.props.record.resId]) - .then((response) => { - this.allInstitutions = response; - }) - .finally(() => { - this.state.isLoading = false; - this.computeGrid(); - }); - } - - async connectBank(institutionId=null) { - const action = await this.orm.call("account.online.link", "action_new_synchronization", [[]], { - preferred_inst: institutionId, - journal_id: this.props.record.resId, - }) - this.action.doAction(action); - } - - async fallbackConnectBank() { - const action = await this.orm.call('account.online.link', 'create_new_bank_account_action', [], { - context: { - active_model: 'account.journal', - active_id: this.props.record.resId, - } - }); - this.action.doAction(action); - } -} - -export const bankConfigureWidget = { - component: BankConfigureWidget, -} - -registry.category("view_widgets").add("bank_configure", bankConfigureWidget); diff --git a/dev_odex30_accounting/odex30_account_online_sync/static/src/components/bank_configure/bank_configure.scss b/dev_odex30_accounting/odex30_account_online_sync/static/src/components/bank_configure/bank_configure.scss deleted file mode 100644 index 3b96ed6..0000000 --- a/dev_odex30_accounting/odex30_account_online_sync/static/src/components/bank_configure/bank_configure.scss +++ /dev/null @@ -1,15 +0,0 @@ -.bank_configure_container { - .d-grid { - overflow: hidden; - column-gap: 0.25rem; - } - .dashboard_bank { - aspect-ratio: 1 / 1; - .align-self-center { - background-color: $gray-100; - border: 1px solid $gray-100; - } - margin-bottom: 0.25rem; - overflow: hidden; - } -} diff --git a/dev_odex30_accounting/odex30_account_online_sync/static/src/components/bank_configure/bank_configure.xml b/dev_odex30_accounting/odex30_account_online_sync/static/src/components/bank_configure/bank_configure.xml deleted file mode 100644 index 5068148..0000000 --- a/dev_odex30_accounting/odex30_account_online_sync/static/src/components/bank_configure/bank_configure.xml +++ /dev/null @@ -1,29 +0,0 @@ - - diff --git a/dev_odex30_accounting/odex30_account_online_sync/static/src/components/bank_reconciliation/fetch_missing_transactions_cog_menu.js b/dev_odex30_accounting/odex30_account_online_sync/static/src/components/bank_reconciliation/fetch_missing_transactions_cog_menu.js deleted file mode 100644 index 6899842..0000000 --- a/dev_odex30_accounting/odex30_account_online_sync/static/src/components/bank_reconciliation/fetch_missing_transactions_cog_menu.js +++ /dev/null @@ -1,68 +0,0 @@ -/** @odoo-module **/ - -import { Component } from "@odoo/owl"; -import { DropdownItem } from "@web/core/dropdown/dropdown_item"; -import { registry } from "@web/core/registry"; -import { useService } from "@web/core/utils/hooks"; - -const cogMenuRegistry = registry.category("cogMenu"); - -/** - * 'Fetch Missing Transactions' menu - * - * This component is used to open a wizard allowing the user to fetch their missing/pending - * transaction since a specific date. - * It's only available in the bank reconciliation widget. - * By default, if there is only one selected journal, this journal is directly selected. - * In case there is no selected journal or more than one, we let the user choose which - * journal he/she wants. This part is handled by the server. - * @extends Component - */ -export class FetchMissingTransactions extends Component { - static template = "odex30_account_online_sync.FetchMissingTransactions"; - static components = { DropdownItem }; - static props = {}; - - setup() { - this.action = useService("action"); - } - - //--------------------------------------------------------------------- - // Protected - //--------------------------------------------------------------------- - - async openFetchMissingTransactionsWizard() { - const { context } = this.env.searchModel; - const activeModel = context.active_model; - let activeIds = []; - if (activeModel === "account.journal") { - activeIds = context.active_ids; - } else if (!!context.default_journal_id) { - activeIds = context.default_journal_id; - } - // We have to use this.env.services.orm.call instead of using useService - // for a specific reason. useService implies that function calls with - // are "protected", it means that if the component is closed the - // response will be pending and the code stop their execution. - // By passing directly from the env, this protection is not activated. - const action = await this.env.services.orm.call( - "account.journal", - "action_open_missing_transaction_wizard", - [activeIds] - ); - return this.action.doAction(action); - } -} - -export const fetchMissingTransactionItem = { - Component: FetchMissingTransactions, - groupNumber: 5, - isDisplayed: ({ config, isSmall }) => { - return !isSmall && - config.actionType === "ir.actions.act_window" && - ["kanban", "list"].includes(config.viewType) && - ["bank_rec_widget_kanban", "bank_rec_list"].includes(config.viewSubType); - }, -}; - -cogMenuRegistry.add("fetch-missing-transaction-menu", fetchMissingTransactionItem, { sequence: 1 }); diff --git a/dev_odex30_accounting/odex30_account_online_sync/static/src/components/bank_reconciliation/fetch_missing_transactions_cog_menu.xml b/dev_odex30_accounting/odex30_account_online_sync/static/src/components/bank_reconciliation/fetch_missing_transactions_cog_menu.xml deleted file mode 100644 index 07256ff..0000000 --- a/dev_odex30_accounting/odex30_account_online_sync/static/src/components/bank_reconciliation/fetch_missing_transactions_cog_menu.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - Find Missing Transactions - - - diff --git a/dev_odex30_accounting/odex30_account_online_sync/static/src/components/bank_reconciliation/find_duplicate_transactions_cog_menu.js b/dev_odex30_accounting/odex30_account_online_sync/static/src/components/bank_reconciliation/find_duplicate_transactions_cog_menu.js deleted file mode 100644 index dc2968e..0000000 --- a/dev_odex30_accounting/odex30_account_online_sync/static/src/components/bank_reconciliation/find_duplicate_transactions_cog_menu.js +++ /dev/null @@ -1,64 +0,0 @@ -import { Component } from "@odoo/owl"; -import { DropdownItem } from "@web/core/dropdown/dropdown_item"; -import { registry } from "@web/core/registry"; -import { useService } from "@web/core/utils/hooks"; - -/** - * 'Find Duplicate Transactions' menu - * - * This component is used to open a wizard allowing the user to find duplicate - * transactions since a specific date. - * It's only available in the bank reconciliation widget. - * By default, if there is only one selected journal, this journal is directly selected. - * In case there is no selected journal or more than one, we let the user choose. - * @extends Component - */ -export class FindDuplicateTransactions extends Component { - static template = "odex30_account_online_sync.FindDuplicateTransactions"; - static components = { DropdownItem }; - static props = {}; - - setup() { - this.action = useService("action"); - } - - //--------------------------------------------------------------------- - // Protected - //--------------------------------------------------------------------- - - async openFindDuplicateTransactionsWizard() { - const { context } = this.env.searchModel; - const activeModel = context.active_model; - let activeIds = []; - if (activeModel === "account.journal") { - activeIds = context.active_ids; - } else if (context.default_journal_id) { - activeIds = context.default_journal_id; - } - return this.action.doActionButton({ - type: "object", - resModel: "account.journal", - name:"action_open_duplicate_transaction_wizard", - resIds: activeIds, - }) - } -} - -export const findDuplicateTransactionItem = { - Component: FindDuplicateTransactions, - groupNumber: 5, // same group as fetch missing transactions - isDisplayed: ({ config, isSmall }) => { - return ( - !isSmall && - config.actionType === "ir.actions.act_window" && - ["kanban", "list"].includes(config.viewType) && - ["bank_rec_widget_kanban", "bank_rec_list"].includes(config.viewSubType) - ) - }, -}; - -registry.category("cogMenu").add( - "find-duplicate-transaction-menu", - findDuplicateTransactionItem, - { sequence: 3 }, // after fetch missing transactions -); diff --git a/dev_odex30_accounting/odex30_account_online_sync/static/src/components/bank_reconciliation/find_duplicate_transactions_cog_menu.xml b/dev_odex30_accounting/odex30_account_online_sync/static/src/components/bank_reconciliation/find_duplicate_transactions_cog_menu.xml deleted file mode 100644 index 1e75c99..0000000 --- a/dev_odex30_accounting/odex30_account_online_sync/static/src/components/bank_reconciliation/find_duplicate_transactions_cog_menu.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - Find Duplicate Transactions - - - diff --git a/dev_odex30_accounting/odex30_account_online_sync/static/src/components/connected_until_widget/connected_until_widget.js b/dev_odex30_accounting/odex30_account_online_sync/static/src/components/connected_until_widget/connected_until_widget.js deleted file mode 100644 index 4792386..0000000 --- a/dev_odex30_accounting/odex30_account_online_sync/static/src/components/connected_until_widget/connected_until_widget.js +++ /dev/null @@ -1,61 +0,0 @@ -/** @odoo-module **/ - -import { registry } from "@web/core/registry"; -import { standardWidgetProps } from "@web/views/widgets/standard_widget_props"; -import { useService } from "@web/core/utils/hooks"; -import { Component, useState } from "@odoo/owl"; - -class ConnectedUntil extends Component { - static template = "odex30_account_online_sync.ConnectedUntil"; - static props = { ...standardWidgetProps }; - - setup() { - this.state = useState({ - isHovered: false, - displayReconnectButton: false, - }); - - if (this.isConnectionExpiredIn(0)) { - this.state.displayReconnectButton = true; - } - - this.action = useService("action"); - this.orm = useService("orm"); - } - - get cssClasses() { - let cssClasses = "text-nowrap w-100"; - if (this.isConnectionExpiredIn(7)) { - cssClasses += this.isConnectionExpiredIn(3) ? " text-danger" : " text-warning"; - } - return cssClasses; - } - - onMouseEnter() { - this.state.isHovered = true; - } - - onMouseLeave() { - this.state.isHovered = false; - } - - isConnectionExpiredIn(nbDays) { - return this.props.record.data.expiring_synchronization_due_day <= nbDays; - } - - async extendConnection() { - const action = await this.orm.call( - "account.journal", - "action_extend_consent", - [this.props.record.resId], - {} - ); - this.action.doAction(action); - } -} - -export const connectedUntil = { - component: ConnectedUntil, -}; - -registry.category("view_widgets").add("connected_until_widget", connectedUntil); diff --git a/dev_odex30_accounting/odex30_account_online_sync/static/src/components/connected_until_widget/connected_until_widget.xml b/dev_odex30_accounting/odex30_account_online_sync/static/src/components/connected_until_widget/connected_until_widget.xml deleted file mode 100644 index 18714c9..0000000 --- a/dev_odex30_accounting/odex30_account_online_sync/static/src/components/connected_until_widget/connected_until_widget.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - -
- - - - - - - Connected until - - - - Extend Connection - - - -
-
-
diff --git a/dev_odex30_accounting/odex30_account_online_sync/static/src/components/online_account_radio/online_account_radio.js b/dev_odex30_accounting/odex30_account_online_sync/static/src/components/online_account_radio/online_account_radio.js deleted file mode 100644 index a0e1aa0..0000000 --- a/dev_odex30_accounting/odex30_account_online_sync/static/src/components/online_account_radio/online_account_radio.js +++ /dev/null @@ -1,41 +0,0 @@ -/** @odoo-module **/ - -import { onMounted, useState } from "@odoo/owl"; -import { registry } from "@web/core/registry"; -import { RadioField, radioField } from "@web/views/fields/radio/radio_field"; -import { useService } from '@web/core/utils/hooks'; - - -class OnlineAccountRadio extends RadioField { - static template = "odex30_account_online_sync.OnlineAccountRadio"; - setup() { - super.setup(); - this.orm = useService("orm"); - this.state = useState({balances: {}}); - - onMounted(async () => { - this.state.balances = await this.loadData(); - // Make sure the first option is selected by default. - this.onChange(this.items[0]); - }); - } - - async loadData() { - const ids = this.items.map(i => i[0]); - return await this.orm.call("account.online.account", "get_formatted_balances", [ids]); - } - - getBalanceName(itemID) { - return this.state.balances?.[itemID]?.[0] ?? "Loading ..."; - } - - isNegativeAmount(itemID) { - // In case of the value is undefined, it will return false as intended. - return this.state.balances?.[itemID]?.[1] < 0; - } -} - -registry.category("fields").add("online_account_radio", { - ...radioField, - component: OnlineAccountRadio, -}); diff --git a/dev_odex30_accounting/odex30_account_online_sync/static/src/components/online_account_radio/online_account_radio.xml b/dev_odex30_accounting/odex30_account_online_sync/static/src/components/online_account_radio/online_account_radio.xml deleted file mode 100644 index 2407b88..0000000 --- a/dev_odex30_accounting/odex30_account_online_sync/static/src/components/online_account_radio/online_account_radio.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - -
- -
- -
-
-
-
-
diff --git a/dev_odex30_accounting/odex30_account_online_sync/static/src/components/refresh_spin_journal_widget/refresh_spin_journal_widget.js b/dev_odex30_accounting/odex30_account_online_sync/static/src/components/refresh_spin_journal_widget/refresh_spin_journal_widget.js deleted file mode 100644 index 8944dfc..0000000 --- a/dev_odex30_accounting/odex30_account_online_sync/static/src/components/refresh_spin_journal_widget/refresh_spin_journal_widget.js +++ /dev/null @@ -1,99 +0,0 @@ -/** @odoo-module **/ - -import { registry } from "@web/core/registry"; -import { standardWidgetProps } from "@web/views/widgets/standard_widget_props"; -import { useService } from "@web/core/utils/hooks"; -import { Component, useState, onWillStart, markup } from "@odoo/owl"; - -class RefreshSpin extends Component { - static template = "odex30_account_online_sync.RefreshSpin"; - static props = { ...standardWidgetProps }; - - setup() { - this.state = useState({ - isHovered: false, - fetchingStatus: false, - connectionStateDetails: null, - }); - - this.actionService = useService("action"); - this.busService = this.env.services.bus_service; - this.orm = useService("orm"); - this.state.fetchingStatus = this.props.record.data.online_sync_fetching_status; - - this.busService.subscribe("online_sync", (notification) => { - if (notification?.id === this.recordId && notification?.connection_state_details) { - this.state.connectionStateDetails = notification.connection_state_details; - } - }); - - onWillStart(() => { - this._initConnectionStateDetails(); - }); - } - - refresh() { - this.actionService.restore(this.actionService.currentController.jsId); - } - - onMouseEnter() { - this.state.isHovered = true; - } - - onMouseLeave() { - this.state.isHovered = false; - } - - async openAction() { - /** - * This function is used to open the action that the asynchronous process saved - * on the databsase. It allows users to call the action when they want and not when - * the process is over. - */ - const action = await this.orm.call( - "account.journal", - "action_open_dashboard_asynchronous_action", - [this.recordId], - ); - this.actionService.doAction(action); - this.state.connectionStateDetails = null; - } - - async fetchTransactions() { - /** - * This function call the function to fetch transactions. - * In the main case, we don't do anything after calling the function. - * The idea is that websockets will update the status by themselves. - * In one specific case, we have to return an action to the user to open - * the Odoo Fin iframe to refresh the connection. - */ - this.state.connectionStateDetails = { status: "fetching" }; - const action = await this.orm.call("account.journal", "manual_sync", [this.recordId]); - if (action) { - action.help = markup(action.help); - this.actionService.doAction(action); - } - } - - _initConnectionStateDetails() { - /** - * This function is used to get the last state of the connection (if there is one) - */ - const kanbanDashboardData = JSON.parse(this.props.record.data.kanban_dashboard); - this.state.connectionStateDetails = kanbanDashboardData?.connection_state_details; - } - - get recordId() { - return this.props.record.data.id; - } - - get connectionStatus() { - return this.state.connectionStateDetails?.status; - } -} - -export const refreshSpin = { - component: RefreshSpin, -}; - -registry.category("view_widgets").add("refresh_spin_widget", refreshSpin); diff --git a/dev_odex30_accounting/odex30_account_online_sync/static/src/components/refresh_spin_journal_widget/refresh_spin_journal_widget.xml b/dev_odex30_accounting/odex30_account_online_sync/static/src/components/refresh_spin_journal_widget/refresh_spin_journal_widget.xml deleted file mode 100644 index 869292b..0000000 --- a/dev_odex30_accounting/odex30_account_online_sync/static/src/components/refresh_spin_journal_widget/refresh_spin_journal_widget.xml +++ /dev/null @@ -1,42 +0,0 @@ - - - - -
- - - transactions fetched - - - - 0 transaction fetched - -
-
- -
- - See error - - -
-
- -
- - Refresh - - - Fetching... - -
-
- - - Fetch Transactions - - -
-
diff --git a/dev_odex30_accounting/odex30_account_online_sync/static/src/components/transient_bank_statement_line_list_view/transient_bank_statement_line_list_view.js b/dev_odex30_accounting/odex30_account_online_sync/static/src/components/transient_bank_statement_line_list_view/transient_bank_statement_line_list_view.js deleted file mode 100644 index 6b9993e..0000000 --- a/dev_odex30_accounting/odex30_account_online_sync/static/src/components/transient_bank_statement_line_list_view/transient_bank_statement_line_list_view.js +++ /dev/null @@ -1,51 +0,0 @@ -/** @odoo-module **/ - -import { ListRenderer } from "@web/views/list/list_renderer"; -import { ListController } from "@web/views/list/list_controller"; -import { registry } from "@web/core/registry"; -import { listView } from "@web/views/list/list_view"; -import { useService } from "@web/core/utils/hooks"; - -export class TransientBankStatementLineListController extends ListController { - - setup() { - super.setup(); - this.orm = useService("orm"); - this.action = useService("action"); - } - - async onClickImportTransactions() { - const resIds = await this.getSelectedResIds(); - const resultAction = await this.orm.call("account.bank.statement.line.transient", "action_import_transactions", [resIds]); - this.action.doAction(resultAction); - } -} - -export class TransientBankStatementLineListRenderer extends ListRenderer { - - static template = "odex30_account_online_sync.TransientBankStatementLineRenderer"; - - setup() { - super.setup(); - this.orm = useService("orm"); - this.action = useService("action"); - } - - async openManualEntries() { - if (this.env.searchModel.context.active_model === "account.missing.transaction.wizard" && this.env.searchModel.context.active_ids) { - const activeIds = this.env.searchModel.context.active_ids; - const action = await this.orm.call("account.missing.transaction.wizard", "action_open_manual_bank_statement_lines", activeIds); - this.action.doAction(action); - } - } - -} - -export const TransientBankStatementLineListView = { - ...listView, - Renderer: TransientBankStatementLineListRenderer, - Controller: TransientBankStatementLineListController, - buttonTemplate: "TransientBankStatementLineButtonTemplate", -} - -registry.category("views").add("transient_bank_statement_line_list_view", TransientBankStatementLineListView); diff --git a/dev_odex30_accounting/odex30_account_online_sync/static/src/components/transient_bank_statement_line_list_view/transient_bank_statement_line_list_view.xml b/dev_odex30_accounting/odex30_account_online_sync/static/src/components/transient_bank_statement_line_list_view/transient_bank_statement_line_list_view.xml deleted file mode 100644 index 1e9c17c..0000000 --- a/dev_odex30_accounting/odex30_account_online_sync/static/src/components/transient_bank_statement_line_list_view/transient_bank_statement_line_list_view.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/dev_odex30_accounting/odex30_account_online_sync/static/src/components/views/account_online_authorization_kanban.js b/dev_odex30_accounting/odex30_account_online_sync/static/src/components/views/account_online_authorization_kanban.js deleted file mode 100644 index bd42d29..0000000 --- a/dev_odex30_accounting/odex30_account_online_sync/static/src/components/views/account_online_authorization_kanban.js +++ /dev/null @@ -1,19 +0,0 @@ -import { patch } from "@web/core/utils/patch"; -import { BankRecKanbanController } from "@odex30_account_accountant/components/bank_reconciliation/kanban"; - -patch(BankRecKanbanController.prototype, { - setup() { - super.setup(); - this.displayDuplicateWarning = !!this.props.context.duplicates_from_date; - }, - async onWarningClick () { - const { context } = this.env.searchModel; - return this.action.doActionButton({ - type: "object", - resModel: "account.journal", - name:"action_open_duplicate_transaction_wizard", - resId: this.state.journalId, - args: JSON.stringify([context.duplicates_from_date]), - }) - }, -}) diff --git a/dev_odex30_accounting/odex30_account_online_sync/static/src/components/views/account_online_authorization_kanban_controller.xml b/dev_odex30_accounting/odex30_account_online_sync/static/src/components/views/account_online_authorization_kanban_controller.xml deleted file mode 100644 index 6cdfe99..0000000 --- a/dev_odex30_accounting/odex30_account_online_sync/static/src/components/views/account_online_authorization_kanban_controller.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - diff --git a/dev_odex30_accounting/odex30_account_online_sync/static/src/js/odoo_fin_connector.js b/dev_odex30_accounting/odex30_account_online_sync/static/src/js/odoo_fin_connector.js deleted file mode 100644 index b8d18ca..0000000 --- a/dev_odex30_accounting/odex30_account_online_sync/static/src/js/odoo_fin_connector.js +++ /dev/null @@ -1,86 +0,0 @@ -/** @odoo-module **/ - -import { registry } from "@web/core/registry"; -import { loadJS } from "@web/core/assets"; -import { cookie } from "@web/core/browser/cookie"; -import { markup } from "@odoo/owl"; -const actionRegistry = registry.category('actions'); -/* global OdooFin */ - -function OdooFinConnector(parent, action) { - const orm = parent.services.orm; - const actionService = parent.services.action; - const notificationService = parent.services.notification; - const debugMode = parent.debug; - - const id = action.id; - action.params.colorScheme = cookie.get("color_scheme"); - let mode = action.params.mode || 'link'; - // Ensure that the proxyMode is valid - const modeRegexp = /^[a-z0-9-_]+$/; - const runbotRegexp = /^https:\/\/[a-z0-9-_]+\.[a-z0-9-_]+\.odoo\.com$/; - if (!modeRegexp.test(action.params.proxyMode) && !runbotRegexp.test(action.params.proxyMode)) { - return; - } - let url = 'https://' + action.params.proxyMode + '.odoofin.com/proxy/v1/odoofin_link'; - if (runbotRegexp.test(action.params.proxyMode)) { - url = action.params.proxyMode + '/proxy/v1/odoofin_link'; - } - let actionResult = false; - - loadJS(url) - .then(function () { - // Create and open the iframe - const params = { - data: action.params, - proxyMode: action.params.proxyMode, - onEvent: async function (event, data) { - switch (event) { - case 'close': - return; - case 'reload': - return actionService.doAction({type: 'ir.actions.client', tag: 'reload'}); - case 'notification': - notificationService.add(data.message, data); - break; - case 'exchange_token': - await orm.call('account.online.link', 'exchange_token', - [[id], data], {context: action.context}); - break; - case 'success': - mode = data.mode || mode; - actionResult = await orm.call('account.online.link', 'success', [[id], mode, data], {context: action.context}); - actionResult.help = markup(actionResult.help) - return actionService.doAction(actionResult); - case 'connect_existing_account': - actionResult = await orm.call('account.online.link', 'connect_existing_account', [data], {context: action.context}); - actionResult.help = markup(actionResult.help) - return actionService.doAction(actionResult); - default: - return; - } - }, - onAddBank: async function (data) { - // If the user doesn't find his bank - actionResult = await orm.call( - "account.online.link", - "create_new_bank_account_action", - [[id], data], - { context: action.context } - ); - return actionService.doAction(actionResult); - } - }; - // propagate parent debug mode to iframe - if (typeof debugMode !== "undefined" && debugMode) { - params.data["debug"] = debugMode; - } - OdooFin.create(params); - OdooFin.open(); - }); - return; -} - -actionRegistry.add('odoo_fin_connector', OdooFinConnector); - -export default OdooFinConnector; diff --git a/dev_odex30_accounting/odex30_account_online_sync/static/src/js/online_sync_portal.js b/dev_odex30_accounting/odex30_account_online_sync/static/src/js/online_sync_portal.js deleted file mode 100644 index 8dde787..0000000 --- a/dev_odex30_accounting/odex30_account_online_sync/static/src/js/online_sync_portal.js +++ /dev/null @@ -1,57 +0,0 @@ -/** @odoo-module **/ - - import publicWidget from "@web/legacy/js/public/public_widget"; - import { loadJS } from "@web/core/assets"; - /* global OdooFin */ - - publicWidget.registry.OnlineSyncPortal = publicWidget.Widget.extend({ - selector: '.oe_online_sync', - events: Object.assign({}, { - 'click #renew_consent_button': '_onRenewConsent', - }), - - OdooFinConnector: function (parent, action) { - // Ensure that the proxyMode is valid - const modeRegexp = /^[a-z0-9-_]+$/i; - if (!modeRegexp.test(action.params.proxyMode)) { - return; - } - const url = 'https://' + action.params.proxyMode + '.odoofin.com/proxy/v1/odoofin_link'; - - loadJS(url) - .then(() => { - // Create and open the iframe - const params = { - data: action.params, - proxyMode: action.params.proxyMode, - onEvent: function (event, data) { - switch (event) { - case 'success': - const processUrl = window.location.pathname + '/complete' + window.location.search; - $('.js_reconnect').toggleClass('d-none'); - $.post(processUrl, {csrf_token: odoo.csrf_token}); - default: - return; - } - }, - }; - OdooFin.create(params); - OdooFin.open(); - }); - return; - }, - - /** - * @private - * @param {Event} ev - */ - _onRenewConsent: async function (ev) { - ev.preventDefault(); - const action = JSON.parse($(ev.currentTarget).attr('iframe-params')); - return this.OdooFinConnector(this, action); - }, - }); - - export default { - OnlineSyncPortal: publicWidget.registry.OnlineSyncPortal, - }; diff --git a/dev_odex30_accounting/odex30_account_online_sync/static/tests/helpers/model_definitions_setup.js b/dev_odex30_accounting/odex30_account_online_sync/static/tests/helpers/model_definitions_setup.js deleted file mode 100644 index 6434c51..0000000 --- a/dev_odex30_accounting/odex30_account_online_sync/static/tests/helpers/model_definitions_setup.js +++ /dev/null @@ -1,5 +0,0 @@ -/** @odoo-module **/ - -import { addModelNamesToFetch } from '@bus/../tests/helpers/model_definitions_helpers'; - -addModelNamesToFetch(["account.online.link", "account.online.account", "account.bank.selection"]); diff --git a/dev_odex30_accounting/odex30_account_online_sync/static/tests/online_account_radio_test.js b/dev_odex30_accounting/odex30_account_online_sync/static/tests/online_account_radio_test.js deleted file mode 100644 index 3be8ad5..0000000 --- a/dev_odex30_accounting/odex30_account_online_sync/static/tests/online_account_radio_test.js +++ /dev/null @@ -1,83 +0,0 @@ -/* @odoo-module */ - -import { startServer } from "@bus/../tests/helpers/mock_python_environment"; - -import { openFormView, start } from "@mail/../tests/helpers/test_utils"; - -import { click, contains } from "@web/../tests/utils"; - -QUnit.module("Views", {}, function () { - QUnit.module("AccountOnlineSynchronizationAccountRadio"); - - QUnit.test("can be rendered", async () => { - const pyEnv = await startServer(); - const onlineLink = pyEnv["account.online.link"].create([ - { - state: "connected", - name: "Fake Bank", - }, - ]); - pyEnv["account.online.account"].create([ - { - name: "account_1", - online_identifier: "abcd", - balance: 10.0, - account_number: "account_number_1", - account_online_link_id: onlineLink, - }, - { - name: "account_2", - online_identifier: "efgh", - balance: 20.0, - account_number: "account_number_2", - account_online_link_id: onlineLink, - }, - ]); - const bankSelection = pyEnv["account.bank.selection"].create([ - { - account_online_link_id: onlineLink, - }, - ]); - - const views = { - "account.bank.selection,false,form": `
-
- - -
-
`, - }; - await start({ - serverData: { views }, - mockRPC: function (route, args) { - if ( - route === "/web/dataset/call_kw/account.online.account/get_formatted_balances" - ) { - return { - 1: ["$ 10.0", 10.0], - 2: ["$ 20.0", 20.0], - }; - } - }, - }); - await openFormView("account.bank.selection", bankSelection); - await contains(".o_radio_item", { count: 2 }); - await contains(":nth-child(1 of .o_radio_item)", { - contains: [ - ["p", { text: "$ 10.0" }], - ["label", { text: "account_1" }], - [".o_radio_input:checked"], - ], - }); - await contains(":nth-child(2 of .o_radio_item)", { - contains: [ - ["p", { text: "$ 20.0" }], - ["label", { text: "account_2" }], - [".o_radio_input:not(:checked)"], - ], - }); - await click(":nth-child(2 of .o_radio_item) .o_radio_input"); - await contains(":nth-child(1 of .o_radio_item) .o_radio_input:not(:checked)"); - await contains(":nth-child(2 of .o_radio_item) .o_radio_input:checked"); - }); -}); diff --git a/dev_odex30_accounting/odex30_account_online_sync/tests/__init__.py b/dev_odex30_accounting/odex30_account_online_sync/tests/__init__.py deleted file mode 100644 index eacefb9..0000000 --- a/dev_odex30_accounting/odex30_account_online_sync/tests/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -# -*- encoding: utf-8 -*- - -from . import common -from . import test_account_online_account -from . import test_online_sync_creation_statement -from . import test_account_missing_transactions_wizard -from . import test_online_sync_branch_companies diff --git a/dev_odex30_accounting/odex30_account_online_sync/tests/common.py b/dev_odex30_accounting/odex30_account_online_sync/tests/common.py deleted file mode 100644 index ffcbc53..0000000 --- a/dev_odex30_accounting/odex30_account_online_sync/tests/common.py +++ /dev/null @@ -1,110 +0,0 @@ -# Part of Odoo. See LICENSE file for full copyright and licensing details. - -from odoo import Command, fields -from odoo.addons.account.tests.common import AccountTestInvoicingCommon -from odoo.tests import tagged -from unittest.mock import MagicMock - - -@tagged('post_install', '-at_install') -class AccountOnlineSynchronizationCommon(AccountTestInvoicingCommon): - - @classmethod - def setUpClass(cls): - super().setUpClass() - cls.other_currency = cls.setup_other_currency('EUR') - - cls.euro_bank_journal = cls.env['account.journal'].create({ - 'name': 'Euro Bank Journal', - 'type': 'bank', - 'code': 'EURB', - 'currency_id': cls.other_currency.id, - }) - cls.account_online_link = cls.env['account.online.link'].create({ - 'name': 'Test Bank', - 'client_id': 'client_id_1', - 'refresh_token': 'refresh_token', - 'access_token': 'access_token', - }) - cls.account_online_account = cls.env['account.online.account'].create({ - 'name': 'MyBankAccount', - 'account_online_link_id': cls.account_online_link.id, - 'journal_ids': [Command.set(cls.euro_bank_journal.id)] - }) - cls.BankStatementLine = cls.env['account.bank.statement.line'] - - def setUp(self): - super().setUp() - self.transaction_id = 1 - self.account_online_account.balance = 0.0 - - def _create_one_online_transaction(self, transaction_identifier=None, date=None, payment_ref=None, amount=10.0, partner_name=None, foreign_currency_code=None, amount_currency=8.0): - """ This method allows to create an online transaction granularly - - :param transaction_identifier: Online identifier of the transaction, by default transaction_id from the - setUp. If used, transaction_id is not incremented. - :param date: Date of the transaction, by default the date of today - :param payment_ref: Label of the transaction - :param amount: Amount of the transaction, by default equals 10.0 - :param foreign_currency_code: Code of transaction's foreign currency - :param amount_currency: Amount of transaction in foreign currency, update transaction only if foreign_currency_code is given, by default equals 8.0 - :return: A dictionnary representing an online transaction (not formatted) - """ - transaction_identifier = transaction_identifier if transaction_identifier is not None else self.transaction_id - if date: - date = date if isinstance(date, str) else fields.Date.to_string(date) - else: - date = fields.Date.to_string(fields.Date.today()) - - payment_ref = payment_ref or f'transaction_{transaction_identifier}' - transaction = { - 'online_transaction_identifier': transaction_identifier, - 'date': date, - 'payment_ref': payment_ref, - 'amount': amount, - 'partner_name': partner_name, - } - if foreign_currency_code: - transaction.update({ - 'foreign_currency_code': foreign_currency_code, - 'amount_currency': amount_currency - }) - return transaction - - def _create_online_transactions(self, dates): - """ This method returns a list of transactions with the - given dates. - All amounts equals 10.0 - - :param dates: A list of dates, one transaction is created for each given date. - :return: A formatted list of transactions - """ - transactions = [] - for date in dates: - transactions.append(self._create_one_online_transaction(date=date)) - self.transaction_id += 1 - return self.account_online_account._format_transactions(transactions) - - def _mock_odoofin_response(self, data=None): - if not data: - data = {} - mock_response = MagicMock() - mock_response.status_code = 200 - mock_response.json.return_value = { - 'result': data, - } - return mock_response - - def _mock_odoofin_error_response(self, code=200, message='Default', data=None): - if not data: - data = {} - mock_response = MagicMock() - mock_response.status_code = 200 - mock_response.json.return_value = { - 'error': { - 'code': code, - 'message': message, - 'data': data, - }, - } - return mock_response diff --git a/dev_odex30_accounting/odex30_account_online_sync/tests/test_account_missing_transactions_wizard.py b/dev_odex30_accounting/odex30_account_online_sync/tests/test_account_missing_transactions_wizard.py deleted file mode 100644 index 1d9989f..0000000 --- a/dev_odex30_accounting/odex30_account_online_sync/tests/test_account_missing_transactions_wizard.py +++ /dev/null @@ -1,45 +0,0 @@ -from odoo import fields -from odoo.addons.odex30_account_online_sync.tests.common import AccountOnlineSynchronizationCommon -from odoo.tests import tagged -from unittest.mock import patch - - -@tagged('post_install', '-at_install') -class TestAccountMissingTransactionsWizard(AccountOnlineSynchronizationCommon): - """ Tests the account journal missing transactions wizard. """ - - @patch('odoo.addons.odex30_account_online_sync.models.account_online.AccountOnlineLink._fetch_odoo_fin') - def test_fetch_missing_transaction(self, patched_fetch_odoofin): - self.account_online_link.state = 'connected' - patched_fetch_odoofin.side_effect = [{ - 'transactions': [ - self._create_one_online_transaction(transaction_identifier='ABCD01', date='2023-07-06', foreign_currency_code='EGP', amount_currency=8.0), - ], - 'pendings': [ - self._create_one_online_transaction(transaction_identifier='ABCD02_pending', date='2023-07-25', foreign_currency_code='GBP', amount_currency=8.0), - ] - }] - start_date = fields.Date.from_string('2023-07-01') - wizard = self.env['account.missing.transaction.wizard'].new({ - 'date': start_date, - 'journal_id': self.euro_bank_journal.id, - }) - - action = wizard.action_fetch_missing_transaction() - transient_transactions = self.env['account.bank.statement.line.transient'].search(domain=action['domain']) - egp_currency = self.env['res.currency'].search([('name', '=', 'EGP')]) - gbp_currency = self.env['res.currency'].search([('name', '=', 'GBP')]) - - self.assertEqual(2, len(transient_transactions)) - # Posted Transaction - self.assertEqual(transient_transactions[0]['online_transaction_identifier'], 'ABCD01') - self.assertEqual(transient_transactions[0]['date'], fields.Date.from_string('2023-07-06')) - self.assertEqual(transient_transactions[0]['state'], 'posted') - self.assertEqual(transient_transactions[0]['foreign_currency_id'], egp_currency) - self.assertEqual(transient_transactions[0]['amount_currency'], 8.0) - # Pending Transaction - self.assertEqual(transient_transactions[1]['online_transaction_identifier'], 'ABCD02_pending') - self.assertEqual(transient_transactions[1]['date'], fields.Date.from_string('2023-07-25')) - self.assertEqual(transient_transactions[1]['state'], 'pending') - self.assertEqual(transient_transactions[1]['foreign_currency_id'], gbp_currency) - self.assertEqual(transient_transactions[1]['amount_currency'], 8.0) diff --git a/dev_odex30_accounting/odex30_account_online_sync/tests/test_account_online_account.py b/dev_odex30_accounting/odex30_account_online_sync/tests/test_account_online_account.py deleted file mode 100644 index 35c5bcd..0000000 --- a/dev_odex30_accounting/odex30_account_online_sync/tests/test_account_online_account.py +++ /dev/null @@ -1,491 +0,0 @@ -# Part of Odoo. See LICENSE file for full copyright and licensing details. - -import logging -from datetime import datetime, timedelta -from freezegun import freeze_time -from unittest.mock import patch - -from odoo import Command, fields, tools -from odoo.addons.odex30_account_online_sync.tests.common import AccountOnlineSynchronizationCommon -from odoo.tests import tagged - -_logger = logging.getLogger(__name__) - -@tagged('post_install', '-at_install') -class TestAccountOnlineAccount(AccountOnlineSynchronizationCommon): - - @classmethod - def setUpClass(cls): - super().setUpClass() - - cls.bank_account_id = cls.env['account.account'].create({ - 'name': 'Bank Account', - 'account_type': 'asset_cash', - 'code': cls.env['account.account']._search_new_account_code('BNK100'), - }) - cls.bank_journal = cls.env['account.journal'].create({ - 'name': 'A bank journal', - 'default_account_id': cls.bank_account_id.id, - 'type': 'bank', - 'code': cls.env['account.journal'].get_next_bank_cash_default_code('bank', cls.company_data['company']), - }) - - @freeze_time('2023-08-01') - def test_get_filtered_transactions(self): - """ This test verifies that duplicate transactions are filtered """ - self.BankStatementLine.with_context(skip_statement_line_cron_trigger=True).create({ - 'date': '2023-08-01', - 'journal_id': self.euro_bank_journal.id, - 'online_transaction_identifier': 'ABCD01', - 'payment_ref': 'transaction_ABCD01', - 'amount': 10.0, - }) - - transactions_to_filtered = [ - self._create_one_online_transaction(transaction_identifier='ABCD01'), - self._create_one_online_transaction(transaction_identifier='ABCD02'), - ] - - filtered_transactions = self.account_online_account._get_filtered_transactions(transactions_to_filtered) - - self.assertEqual( - filtered_transactions, - [ - { - 'payment_ref': 'transaction_ABCD02', - 'date': '2023-08-01', - 'online_transaction_identifier': 'ABCD02', - 'amount': 10.0, - 'partner_name': None, - } - ] - ) - - @freeze_time('2023-08-01') - def test_get_filtered_transactions_with_empty_transaction_identifier(self): - """ This test verifies that transactions without a transaction identifier - are not filtered due to their empty transaction identifier. - """ - self.BankStatementLine.with_context(skip_statement_line_cron_trigger=True).create({ - 'date': '2023-08-01', - 'journal_id': self.euro_bank_journal.id, - 'online_transaction_identifier': '', - 'payment_ref': 'transaction_ABCD01', - 'amount': 10.0, - }) - - transactions_to_filtered = [ - self._create_one_online_transaction(transaction_identifier=''), - self._create_one_online_transaction(transaction_identifier=''), - ] - - filtered_transactions = self.account_online_account._get_filtered_transactions(transactions_to_filtered) - - self.assertEqual( - filtered_transactions, - [ - { - 'payment_ref': 'transaction_', - 'date': '2023-08-01', - 'online_transaction_identifier': '', - 'amount': 10.0, - 'partner_name': None, - }, - { - 'payment_ref': 'transaction_', - 'date': '2023-08-01', - 'online_transaction_identifier': '', - 'amount': 10.0, - 'partner_name': None, - }, - ] - ) - - @freeze_time('2023-08-01') - def test_format_transactions(self): - transactions_to_format = [ - self._create_one_online_transaction(transaction_identifier='ABCD01'), - self._create_one_online_transaction(transaction_identifier='ABCD02'), - ] - formatted_transactions = self.account_online_account._format_transactions(transactions_to_format) - self.assertEqual( - formatted_transactions, - [ - { - 'payment_ref': 'transaction_ABCD01', - 'date': fields.Date.from_string('2023-08-01'), - 'online_transaction_identifier': 'ABCD01', - 'amount': 10.0, - 'online_account_id': self.account_online_account.id, - 'journal_id': self.euro_bank_journal.id, - 'company_id': self.euro_bank_journal.company_id.id, - 'partner_name': None, - }, - { - 'payment_ref': 'transaction_ABCD02', - 'date': fields.Date.from_string('2023-08-01'), - 'online_transaction_identifier': 'ABCD02', - 'amount': 10.0, - 'online_account_id': self.account_online_account.id, - 'journal_id': self.euro_bank_journal.id, - 'company_id': self.euro_bank_journal.company_id.id, - 'partner_name': None, - }, - ] - ) - - @freeze_time('2023-08-01') - def test_format_transactions_invert_sign(self): - transactions_to_format = [ - self._create_one_online_transaction(transaction_identifier='ABCD01', amount=25.0), - ] - self.account_online_account.inverse_transaction_sign = True - formatted_transactions = self.account_online_account._format_transactions(transactions_to_format) - self.assertEqual( - formatted_transactions, - [ - { - 'payment_ref': 'transaction_ABCD01', - 'date': fields.Date.from_string('2023-08-01'), - 'online_transaction_identifier': 'ABCD01', - 'amount': -25.0, - 'online_account_id': self.account_online_account.id, - 'journal_id': self.euro_bank_journal.id, - 'company_id': self.euro_bank_journal.company_id.id, - 'partner_name': None, - }, - ] - ) - - @freeze_time('2023-08-01') - def test_format_transactions_foreign_currency_code_to_id_with_activation(self): - """ This test ensures conversion of foreign currency code to foreign currency id and activates foreign currency if not already activate """ - gbp_currency = self.env['res.currency'].with_context(active_test=False).search([('name', '=', 'GBP')]) - egp_currency = self.env['res.currency'].with_context(active_test=False).search([('name', '=', 'EGP')]) - - transactions_to_format = [ - self._create_one_online_transaction(transaction_identifier='ABCD01', foreign_currency_code='GBP'), - self._create_one_online_transaction(transaction_identifier='ABCD02', foreign_currency_code='EGP', amount_currency=500.0), - ] - formatted_transactions = self.account_online_account._format_transactions(transactions_to_format) - - self.assertTrue(gbp_currency.active) - self.assertTrue(egp_currency.active) - - self.assertEqual( - formatted_transactions, - [ - { - 'payment_ref': 'transaction_ABCD01', - 'date': fields.Date.from_string('2023-08-01'), - 'online_transaction_identifier': 'ABCD01', - 'amount': 10.0, - 'online_account_id': self.account_online_account.id, - 'journal_id': self.euro_bank_journal.id, - 'company_id': self.euro_bank_journal.company_id.id, - 'partner_name': None, - 'foreign_currency_id': gbp_currency.id, - 'amount_currency': 8.0, - }, - { - 'payment_ref': 'transaction_ABCD02', - 'date': fields.Date.from_string('2023-08-01'), - 'online_transaction_identifier': 'ABCD02', - 'amount': 10.0, - 'online_account_id': self.account_online_account.id, - 'journal_id': self.euro_bank_journal.id, - 'company_id': self.euro_bank_journal.company_id.id, - 'partner_name': None, - 'foreign_currency_id': egp_currency.id, - 'amount_currency': 500.0, - }, - ] - ) - - @freeze_time('2023-07-25') - @patch('odoo.addons.odex30_account_online_sync.models.account_online.AccountOnlineLink._fetch_odoo_fin') - def test_retrieve_pending_transactions(self, patched_fetch_odoofin): - self.account_online_link.state = 'connected' - patched_fetch_odoofin.side_effect = [{ - 'transactions': [ - self._create_one_online_transaction(transaction_identifier='ABCD01', date='2023-07-06'), - self._create_one_online_transaction(transaction_identifier='ABCD02', date='2023-07-22'), - ], - 'pendings': [ - self._create_one_online_transaction(transaction_identifier='ABCD03_pending', date='2023-07-25'), - self._create_one_online_transaction(transaction_identifier='ABCD04_pending', date='2023-07-25'), - ] - }] - - start_date = fields.Date.from_string('2023-07-01') - result = self.account_online_account._retrieve_transactions(date=start_date, include_pendings=True) - self.assertEqual( - result, - { - 'transactions': [ - { - 'payment_ref': 'transaction_ABCD01', - 'date': fields.Date.from_string('2023-07-06'), - 'online_transaction_identifier': 'ABCD01', - 'amount': 10.0, - 'partner_name': None, - 'online_account_id': self.account_online_account.id, - 'journal_id': self.euro_bank_journal.id, - 'company_id': self.euro_bank_journal.company_id.id, - }, - { - 'payment_ref': 'transaction_ABCD02', - 'date': fields.Date.from_string('2023-07-22'), - 'online_transaction_identifier': 'ABCD02', - 'amount': 10.0, - 'partner_name': None, - 'online_account_id': self.account_online_account.id, - 'journal_id': self.euro_bank_journal.id, - 'company_id': self.euro_bank_journal.company_id.id, - } - ], - 'pendings': [ - { - 'payment_ref': 'transaction_ABCD03_pending', - 'date': fields.Date.from_string('2023-07-25'), - 'online_transaction_identifier': 'ABCD03_pending', - 'amount': 10.0, - 'partner_name': None, - 'online_account_id': self.account_online_account.id, - 'journal_id': self.euro_bank_journal.id, - 'company_id': self.euro_bank_journal.company_id.id, - }, - { - 'payment_ref': 'transaction_ABCD04_pending', - 'date': fields.Date.from_string('2023-07-25'), - 'online_transaction_identifier': 'ABCD04_pending', - 'amount': 10.0, - 'partner_name': None, - 'online_account_id': self.account_online_account.id, - 'journal_id': self.euro_bank_journal.id, - 'company_id': self.euro_bank_journal.company_id.id, - } - ] - } - ) - - @freeze_time('2023-01-01 01:10:15') - @patch('odoo.addons.odex30_account_online_sync.models.account_online.AccountOnlineAccount._retrieve_transactions', return_value={}) - @patch('odoo.addons.odex30_account_online_sync.models.account_online.AccountOnlineAccount._refresh', return_value={'success': True, 'data': {}}) - def test_basic_flow_manual_fetching_transactions(self, patched_refresh, patched_transactions): - self.addCleanup(self.env.registry.leave_test_mode) - # flush and clear everything for the new "transaction" - self.env.invalidate_all() - - self.env.registry.enter_test_mode(self.cr) - with self.env.registry.cursor() as test_cr: - test_env = self.env(cr=test_cr) - test_link_account = self.account_online_link.with_env(test_env) - test_link_account.state = 'connected' - # Call fetch_transaction in manual mode and check that a call was made to refresh and to transaction - test_link_account._fetch_transactions() - patched_refresh.assert_called_once() - patched_transactions.assert_called_once() - self.assertEqual(test_link_account.account_online_account_ids[0].fetching_status, 'done') - - @freeze_time('2023-01-01 01:10:15') - @patch('odoo.addons.odex30_account_online_sync.models.account_online.AccountOnlineAccount._retrieve_transactions', return_value={}) - @patch('odoo.addons.odex30_account_online_sync.models.account_online.AccountOnlineLink._fetch_odoo_fin') - def test_refresh_incomplete_fetching_transactions(self, patched_refresh, patched_transactions): - patched_refresh.return_value = {'success': False} - # Call fetch_transaction and if call result is false, don't call transaction - self.account_online_link._fetch_transactions() - patched_transactions.assert_not_called() - - patched_refresh.return_value = {'success': False, 'currently_fetching': True} - # Call fetch_transaction and if call result is false but in the process of fetching, don't call transaction - # and wait for the async cron to try again - self.account_online_link._fetch_transactions() - patched_transactions.assert_not_called() - self.assertEqual(self.account_online_account.fetching_status, 'waiting') - - @freeze_time('2023-01-01 01:10:15') - @patch('odoo.addons.odex30_account_online_sync.models.account_online.AccountOnlineAccount._retrieve_transactions', return_value={}) - @patch('odoo.addons.odex30_account_online_sync.models.account_online.AccountOnlineAccount._refresh', return_value={'success': True, 'data': {}}) - def test_currently_processing_fetching_transactions(self, patched_refresh, patched_transactions): - self.account_online_account.fetching_status = 'processing' # simulate the fact that we are currently creating entries in odoo - limit_time = tools.config['limit_time_real_cron'] if tools.config['limit_time_real_cron'] > 0 else tools.config['limit_time_real'] - self.account_online_link.last_refresh = datetime.now() - with freeze_time(datetime.now() + timedelta(seconds=(limit_time - 10))): - # Call to fetch_transaction should be skipped, and the cron should not try to fetch either - self.account_online_link._fetch_transactions() - self.euro_bank_journal._cron_fetch_waiting_online_transactions() - patched_refresh.assert_not_called() - patched_transactions.assert_not_called() - - self.addCleanup(self.env.registry.leave_test_mode) - # flush and clear everything for the new "transaction" - self.env.invalidate_all() - - self.env.registry.enter_test_mode(self.cr) - with self.env.registry.cursor() as test_cr: - test_env = self.env(cr=test_cr) - with freeze_time(datetime.now() + timedelta(seconds=(limit_time + 100))): - # Call to fetch_transaction should be started by the cron when the time limit is exceeded and still in processing - self.euro_bank_journal.with_env(test_env)._cron_fetch_waiting_online_transactions() - patched_refresh.assert_not_called() - patched_transactions.assert_called_once() - - @patch('odoo.addons.odex30_account_online_sync.models.account_online.requests') - def test_delete_with_redirect_error(self, patched_request): - # Use case being tested: call delete on a record, first call returns token expired exception - # Which trigger a call to get a new token, which result in a 104 user_deleted_error, since version 17, - # such error are returned as a OdooFinRedirectException with mode link to reopen the iframe and link with a new - # bank. In our case we don't want that and want to be able to delete the record instead. - # Such use case happen when db_uuid has changed as the check for db_uuid is done after the check for token_validity - account_online_link = self.env['account.online.link'].create({ - 'name': 'Test Delete', - 'client_id': 'client_id_test', - 'refresh_token': 'refresh_token', - 'access_token': 'access_token', - }) - first_call = self._mock_odoofin_error_response(code=102) - second_call = self._mock_odoofin_error_response(code=300, data={'mode': 'link'}) - patched_request.post.side_effect = [first_call, second_call] - nb_connections = len(self.env['account.online.link'].search([])) - # Try to delete record - account_online_link.unlink() - # Record should be deleted - self.assertEqual(len(self.env['account.online.link'].search([])), nb_connections - 1) - - @patch('odoo.addons.odex30_account_online_sync.models.account_online.requests') - def test_redirect_mode_link(self, patched_request): - # Use case being tested: Call to open the iframe which result in a OdoofinRedirectException in link mode - # This should not trigger a traceback but delete the current online.link and reopen the iframe - account_online_link = self.env['account.online.link'].create({ - 'name': 'Test Delete', - 'client_id': 'client_id_test', - 'refresh_token': 'refresh_token', - 'access_token': 'access_token', - }) - link_id = account_online_link.id - first_call = self._mock_odoofin_error_response(code=300, data={'mode': 'link'}) - second_call = self._mock_odoofin_response(data={'delete': True}) - patched_request.post.side_effect = [first_call, second_call] - # Try to open iframe with broken connection - action = account_online_link.action_new_synchronization() - # Iframe should open in mode link and with a different record (old one should have been deleted) - self.assertEqual(action['params']['mode'], 'link') - self.assertNotEqual(action['id'], link_id) - self.assertEqual(len(self.env['account.online.link'].search([('id', '=', link_id)])), 0) - - @patch("odoo.addons.odex30_account_online_sync.models.account_online.AccountOnlineLink._update_connection_status", return_value={}) - def test_assign_journal_with_currency_on_account_online_account(self, patched_update_connection_status): - self.env['account.move'].create([ - { - 'move_type': 'entry', - 'date': fields.Date.from_string('2025-06-25'), - 'journal_id': self.bank_journal.id, - 'invoice_line_ids': [ - Command.create({ - 'name': 'a line', - 'account_id': self.bank_account_id.id, - 'debit': 100, - 'currency_id': self.company_data['currency'].id, - }), - Command.create({ - 'name': 'another line', - 'account_id': self.company_data['default_account_expense'].id, - 'credit': 100, - 'currency_id': self.company_data['currency'].id, - }), - ], - }, - { - 'move_type': 'entry', - 'date': fields.Date.from_string('2025-06-26'), - 'journal_id': self.bank_journal.id, - 'invoice_line_ids': [ - Command.create({ - 'name': 'a line', - 'account_id': self.bank_account_id.id, - 'debit': 220, - 'currency_id': self.company_data['currency'].id, - }), - Command.create({ - 'name': 'another line', - 'account_id': self.company_data['default_account_expense'].id, - 'credit': 220, - 'currency_id': self.company_data['currency'].id, - }), - ], - }, - ]) - - self.account_online_account.currency_id = self.company_data['currency'].id - self.account_online_account.with_context(active_id=self.bank_journal.id, active_model='account.journal')._assign_journal() - self.assertEqual( - self.bank_journal.currency_id.id, - self.company_data['currency'].id, - ) - self.assertEqual( - self.bank_journal.default_account_id.currency_id.id, - self.company_data['currency'].id, - ) - - @patch("odoo.addons.odex30_account_online_sync.models.account_online.AccountOnlineLink._update_connection_status", return_value={}) - def test_set_currency_on_journal_when_existing_currencies_on_move_lines(self, patched_update_connection_status): - bank_account_id = self.env['account.account'].create({ - 'name': 'Bank Account', - 'account_type': 'asset_cash', - 'code': self.env['account.account']._search_new_account_code('BNK100'), - }) - bank_journal = self.env['account.journal'].create({ - 'name': 'A bank journal', - 'default_account_id': bank_account_id.id, - 'type': 'bank', - 'code': self.env['account.journal'].get_next_bank_cash_default_code('bank', self.company_data['company']), - }) - - self.env['account.move'].create([ - { - 'move_type': 'entry', - 'date': fields.Date.from_string('2025-06-25'), - 'journal_id': bank_journal.id, - 'invoice_line_ids': [ - Command.create({ - 'name': 'a line', - 'account_id': bank_account_id.id, - 'debit': 100, - 'currency_id': self.other_currency.id, - }), - Command.create({ - 'name': 'another line', - 'account_id': self.company_data['default_account_expense'].id, - 'credit': 100, - 'currency_id': self.other_currency.id, - }), - ], - }, - { - 'move_type': 'entry', - 'date': fields.Date.from_string('2025-06-26'), - 'journal_id': bank_journal.id, - 'invoice_line_ids': [ - Command.create({ - 'name': 'a line', - 'account_id': bank_account_id.id, - 'debit': 220, - 'currency_id': self.company_data['currency'].id, - }), - Command.create({ - 'name': 'another line', - 'account_id': self.company_data['default_account_expense'].id, - 'credit': 220, - 'currency_id': self.company_data['currency'].id, - }), - ], - }, - ]) - - self.account_online_account.currency_id = self.company_data['currency'].id - self.account_online_account.with_context(active_id=bank_journal.id, active_model='account.journal')._assign_journal() - - # Silently ignore the error and don't set currency on the journal and on the account - self.assertEqual(bank_journal.currency_id.id, False) - self.assertEqual(bank_journal.default_account_id.currency_id.id, False) diff --git a/dev_odex30_accounting/odex30_account_online_sync/tests/test_online_sync_branch_companies.py b/dev_odex30_accounting/odex30_account_online_sync/tests/test_online_sync_branch_companies.py deleted file mode 100644 index 16cb0ef..0000000 --- a/dev_odex30_accounting/odex30_account_online_sync/tests/test_online_sync_branch_companies.py +++ /dev/null @@ -1,86 +0,0 @@ -# Part of Odoo. See LICENSE file for full copyright and licensing details. - -from odoo.addons.odex30_account_online_sync.tests.common import AccountOnlineSynchronizationCommon -from odoo.tests import tagged - - -@tagged('post_install', '-at_install') -class TestSynchInBranches(AccountOnlineSynchronizationCommon): - - @classmethod - def setUpClass(cls): - super().setUpClass() - - cls.mother_company = cls.env['res.company'].create({'name': 'Mother company 2000'}) - cls.branch_company = cls.env['res.company'].create({'name': 'Branch company', 'parent_id': cls.mother_company.id}) - - cls.mother_bank_journal = cls.env['account.journal'].create({ - 'name': 'Mother Bank Journal', - 'type': 'bank', - 'code': 'MBJ', - 'company_id': cls.mother_company.id, - }) - cls.mother_account_online_link = cls.env['account.online.link'].create({ - 'name': 'Test Bank', - 'client_id': 'client_id_1', - 'refresh_token': 'refresh_token', - 'access_token': 'access_token', - 'company_id': cls.mother_company.id, - }) - - def test_show_sync_actions(self): - """We test if the sync actions are correctly displayed based on the selected and enabled companies. - - Let's have company A with an online link, and a branch of that company: company B. - - - If we only have company A enabled and selected, the sync actions should be shown. - - If company A and B are enabled, no matter which company is selected, the sync actions should be shown. - - If we only have company B enabled and selected, the sync actions should be hidden. - """ - self.assertTrue( - self.mother_account_online_link - .with_context(allowed_company_ids=(self.mother_company)._ids) - .with_company(self.mother_company) - .show_sync_actions - ) - - self.assertTrue( - self.mother_account_online_link - .with_context(allowed_company_ids=(self.branch_company + self.mother_company)._ids) - .with_company(self.mother_company) - .show_sync_actions - ) - - self.assertTrue( - self.mother_account_online_link - .with_context(allowed_company_ids=(self.branch_company + self.mother_company)._ids) - .with_company(self.branch_company) - .show_sync_actions - ) - - self.assertFalse( - self.mother_account_online_link - .with_context(allowed_company_ids=(self.branch_company)._ids) - .with_company(self.branch_company) - .show_sync_actions - ) - - def test_show_bank_connect(self): - """We test if the 'connect' bank button appears on the journal on the dashboard given the selected company. - - Let's have company A with an bank journal, and a branch of that company: company B. - - - On the dashboard of company A, the connect bank button should appear on the journal. - - On the dashboard of company B, the connect bank button should not appear on the journal, even with company A enabled. - """ - dashboard_data = self.mother_bank_journal\ - .with_context(allowed_company_ids=(self.mother_company)._ids)\ - .with_company(self.mother_company)\ - ._get_journal_dashboard_data_batched() - self.assertTrue(dashboard_data[self.mother_bank_journal.id].get('display_connect_bank_in_dashboard')) - - dashboard_data = self.mother_bank_journal\ - .with_context(allowed_company_ids=(self.branch_company + self.mother_company)._ids)\ - .with_company(self.branch_company)\ - ._get_journal_dashboard_data_batched() - self.assertFalse(dashboard_data[self.mother_bank_journal.id].get('display_connect_bank_in_dashboard')) diff --git a/dev_odex30_accounting/odex30_account_online_sync/tests/test_online_sync_creation_statement.py b/dev_odex30_accounting/odex30_account_online_sync/tests/test_online_sync_creation_statement.py deleted file mode 100644 index a68e1da..0000000 --- a/dev_odex30_accounting/odex30_account_online_sync/tests/test_online_sync_creation_statement.py +++ /dev/null @@ -1,374 +0,0 @@ -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. -from unittest.mock import MagicMock, patch - -from odoo.addons.base.models.res_bank import sanitize_account_number -from odoo.addons.odex30_account_online_sync.tests.common import AccountOnlineSynchronizationCommon -from odoo.exceptions import RedirectWarning -from odoo.tests import tagged -from odoo import fields, Command - - -@tagged('post_install', '-at_install') -class TestSynchStatementCreation(AccountOnlineSynchronizationCommon): - - @classmethod - def setUpClass(cls): - super().setUpClass() - - cls.account = cls.env['account.account'].create({ - 'name': 'Fixed Asset Account', - 'code': 'AA', - 'account_type': 'asset_fixed', - }) - - def reconcile_st_lines(self, st_lines): - for line in st_lines: - wizard = self.env['bank.rec.widget'].with_context(default_st_line_id=line.id).new({}) - line = wizard.line_ids.filtered(lambda x: x.flag == 'auto_balance') - wizard._js_action_mount_line_in_edit(line.index) - line.name = "toto" - wizard._line_value_changed_name(line) - line.account_id = self.account - wizard._line_value_changed_account_id(line) - wizard._action_validate() - - # Tests - def test_creation_initial_sync_statement(self): - transactions = self._create_online_transactions(['2016-01-01', '2016-01-03']) - self.account_online_account.balance = 1000 - self.BankStatementLine._online_sync_bank_statement(transactions, self.account_online_account) - # Since ending balance is 1000$ and we only have 20$ of transactions and that it is the first statement - # it should create a statement before this one with the initial statement line - created_st_lines = self.BankStatementLine.search([('journal_id', '=', self.euro_bank_journal.id)], order='internal_index asc') - self.assertEqual(len(created_st_lines), 3, 'Should have created an initial bank statement line and two for the synchronization') - transactions = self._create_online_transactions(['2016-01-05']) - self.account_online_account.balance = 2000 - self.BankStatementLine._online_sync_bank_statement(transactions, self.account_online_account) - created_st_lines = self.BankStatementLine.search([('journal_id', '=', self.euro_bank_journal.id)], order='internal_index asc') - self.assertRecordValues( - created_st_lines, - [ - {'date': fields.Date.from_string('2016-01-01'), 'amount': 980.0}, - {'date': fields.Date.from_string('2016-01-01'), 'amount': 10.0}, - {'date': fields.Date.from_string('2016-01-03'), 'amount': 10.0}, - {'date': fields.Date.from_string('2016-01-05'), 'amount': 10.0}, - ] - ) - - def test_creation_initial_sync_statement_bis(self): - transactions = self._create_online_transactions(['2016-01-01', '2016-01-03']) - self.account_online_account.balance = 20 - self.BankStatementLine._online_sync_bank_statement(transactions, self.account_online_account) - # Since ending balance is 20$ and we only have 20$ of transactions and that it is the first statement - # it should NOT create a initial statement before this one - created_st_lines = self.BankStatementLine.search([('journal_id', '=', self.euro_bank_journal.id)], order='internal_index asc') - self.assertRecordValues( - created_st_lines, - [ - {'date': fields.Date.from_string('2016-01-01'), 'amount': 10.0}, - {'date': fields.Date.from_string('2016-01-03'), 'amount': 10.0}, - ] - ) - - def test_creation_initial_sync_statement_invert_sign(self): - self.account_online_account.balance = -20 - self.account_online_account.inverse_transaction_sign = True - self.account_online_account.inverse_balance_sign = True - transactions = self._create_online_transactions(['2016-01-01', '2016-01-03']) - self.BankStatementLine._online_sync_bank_statement(transactions, self.account_online_account) - # Since ending balance is 1000$ and we only have 20$ of transactions and that it is the first statement - # it should create a statement before this one with the initial statement line - created_st_lines = self.BankStatementLine.search([('journal_id', '=', self.euro_bank_journal.id)], order='internal_index asc') - self.assertEqual(len(created_st_lines), 2, 'Should have created two bank statement lines for the synchronization') - transactions = self._create_online_transactions(['2016-01-05']) - self.account_online_account.balance = -30 - self.BankStatementLine._online_sync_bank_statement(transactions, self.account_online_account) - created_st_lines = self.BankStatementLine.search([('journal_id', '=', self.euro_bank_journal.id)], order='internal_index asc') - self.assertRecordValues( - created_st_lines, - [ - {'date': fields.Date.from_string('2016-01-01'), 'amount': -10.0}, - {'date': fields.Date.from_string('2016-01-03'), 'amount': -10.0}, - {'date': fields.Date.from_string('2016-01-05'), 'amount': -10.0}, - ] - ) - - @patch('odoo.addons.odex30_account_online_sync.models.account_online.AccountOnlineLink._fetch_transactions') - @patch('odoo.addons.odex30_account_online_sync.models.account_online.AccountOnlineLink._update_connection_status') - def test_automatic_journal_assignment(self, patched_update_connection_status, patched_fetch_transactions): - def create_online_account(name, link_id, iban, currency_id): - return self.env['account.online.account'].create({ - 'name': name, - 'account_online_link_id': link_id, - 'account_number': iban, - 'currency_id' : currency_id, - }) - - def create_bank_account(account_number, partner_id): - return self.env['res.partner.bank'].create({ - 'acc_number': account_number, - 'partner_id': partner_id, - }) - - def create_journal(name, journal_type, code, currency_id=False, bank_account_id=False): - return self.env['account.journal'].create({ - 'name': name, - 'type': journal_type, - 'code': code, - 'currency_id': currency_id, - 'bank_account_id': bank_account_id, - }) - - bank_account_1 = create_bank_account('BE48485444456727', self.company_data['company'].partner_id.id) - bank_account_2 = create_bank_account('BE23798242487491', self.company_data['company'].partner_id.id) - - bank_journal_with_account_gol = create_journal('Bank with account', 'bank', 'BJWA1', self.other_currency.id) - bank_journal_with_account_usd = create_journal('Bank with account USD', 'bank', 'BJWA3', self.env.ref('base.USD').id, bank_account_2.id) - - online_account_1 = create_online_account('OnlineAccount1', self.account_online_link.id, 'BE48485444456727', self.other_currency.id) - online_account_2 = create_online_account('OnlineAccount2', self.account_online_link.id, 'BE61954856342317', self.other_currency.id) - online_account_3 = create_online_account('OnlineAccount3', self.account_online_link.id, 'BE23798242487495', self.other_currency.id) - - patched_fetch_transactions.return_value = True - patched_update_connection_status.return_value = { - 'consent_expiring_date': None, - 'is_payment_enabled': False, - 'is_payment_activated': False, - } - - account_link_journal_wizard = self.env['account.bank.selection'].create({'account_online_link_id': self.account_online_link.id}) - account_link_journal_wizard.with_context(active_model='account.journal', active_id=bank_journal_with_account_gol.id).sync_now() - self.assertEqual( - online_account_1.id, bank_journal_with_account_gol.account_online_account_id.id, - "The wizard should have linked the online account to the journal with the same account." - ) - self.assertEqual(bank_journal_with_account_gol.bank_account_id, bank_account_1, "Account should be set on the journal") - - # Test with no context present, should create a new journal - previous_number = self.env['account.journal'].search_count([]) - account_link_journal_wizard.selected_account = online_account_2 - account_link_journal_wizard.sync_now() - actual_number = self.env['account.journal'].search_count([]) - self.assertEqual(actual_number, previous_number+1, "should have created a new journal") - self.assertEqual(online_account_2.journal_ids.currency_id, self.other_currency) - self.assertEqual(online_account_2.journal_ids.bank_account_id.sanitized_acc_number, sanitize_account_number('BE61954856342317')) - - # Test assigning to a journal in another currency - account_link_journal_wizard.selected_account = online_account_3 - account_link_journal_wizard.with_context(active_model='account.journal', active_id=bank_journal_with_account_usd.id).sync_now() - self.assertEqual(online_account_3.id, bank_journal_with_account_usd.account_online_account_id.id) - self.assertEqual(bank_journal_with_account_usd.bank_account_id, bank_account_2, "Bank Account should not have changed") - self.assertEqual(bank_journal_with_account_usd.currency_id, self.other_currency, "Currency should have changed") - - @patch('odoo.addons.odex30_account_online_sync.models.account_online.AccountOnlineLink._fetch_odoo_fin') - def test_fetch_transaction_date_start(self, patched_fetch): - """ This test verifies that the start_date params used when fetching transaction is correct """ - patched_fetch.return_value = {'transactions': []} - # Since no transactions exists in db, we should fetch transactions without a starting_date - self.account_online_account._retrieve_transactions() - data = { - 'start_date': False, - 'account_id': False, - 'last_transaction_identifier': False, - 'currency_code': 'EUR', - 'provider_data': False, - 'account_data': False, - 'include_pendings': False, - 'include_foreign_currency': True, - } - patched_fetch.assert_called_with('/proxy/v1/transactions', data=data) - - # No transaction exists in db but we have a value for last_sync on the online_account, we should use that date - self.account_online_account.last_sync = '2020-03-04' - data['start_date'] = '2020-03-04' - self.account_online_account._retrieve_transactions() - patched_fetch.assert_called_with('/proxy/v1/transactions', data=data) - - # We have transactions, we should use the date of the latest one instead of the last_sync date - transactions = self._create_online_transactions(['2016-01-01', '2016-01-03']) - self.BankStatementLine._online_sync_bank_statement(transactions, self.account_online_account) - self.account_online_account.last_sync = '2020-03-04' - data['start_date'] = '2016-01-03' - data['last_transaction_identifier'] = '2' - self.account_online_account._retrieve_transactions() - patched_fetch.assert_called_with('/proxy/v1/transactions', data=data) - - def test_multiple_transaction_identifier_fetched(self): - # Ensure that if we receive twice the same transaction within the same call, it won't be created twice - transactions = self._create_online_transactions(['2016-01-01', '2016-01-03']) - # Add first transactions to the list again - transactions.append(transactions[0]) - self.BankStatementLine._online_sync_bank_statement(transactions, self.account_online_account) - bnk_stmt_lines = self.BankStatementLine.search([('online_transaction_identifier', '!=', False), ('journal_id', '=', self.euro_bank_journal.id)]) - self.assertEqual(len(bnk_stmt_lines), 2, 'Should only have created two lines') - - @patch('odoo.addons.odex30_account_online_sync.models.account_online.AccountOnlineLink._fetch_odoo_fin') - def test_fetch_transactions_reauth(self, patched_refresh): - patched_refresh.side_effect = [ - { - 'success': False, - 'code': 300, - 'data': {'mode': 'updateCredentials'}, - }, - { - 'access_token': 'open_sesame', - }, - ] - self.account_online_account.account_online_link_id.state = 'connected' - res = self.account_online_account.account_online_link_id._fetch_transactions() - self.assertTrue('account_online_identifier' in res.get('params', {}).get('includeParam', {})) - - def test_duplicate_transaction_date_amount_account(self): - """ This test verifies that the duplicate transaction wizard is detects transactions with - same date, amount, account_number and currency - """ - # Create 2 groups of respectively 2 and 3 duplicate transactions. We create one transaction the day before so the opening statement does not interfere with the test. - transactions = self._create_online_transactions([ - '2024-01-01', - '2024-01-02', '2024-01-02', - '2024-01-03', '2024-01-03', '2024-01-03', - ]) - bsls = self.BankStatementLine._online_sync_bank_statement(transactions, self.account_online_account) - self.env.flush_all() # _get_duplicate_transactions make sql request, must write to db - duplicate_transactions = self.euro_bank_journal._get_duplicate_transactions( - fields.Date.to_date('2000-01-01') - ) - group_1 = bsls.filtered(lambda bsl: bsl.date == fields.Date.from_string('2024-01-02')).ids - group_2 = bsls.filtered(lambda bsl: bsl.date == fields.Date.from_string('2024-01-03')).ids - - self.assertEqual(duplicate_transactions, [group_1, group_2]) - - # check has_duplicate_transactions - has_duplicate_transactions = self.euro_bank_journal._has_duplicate_transactions( - fields.Date.to_date('2000-01-01') - ) - self.assertTrue(has_duplicate_transactions is True) # explicit check on bool type - - def test_duplicate_transaction_online_transaction_identifier(self): - """ This test verifies that the duplicate transaction wizard is detects transactions with - same online_transaction_identifier - """ - # Create transactions - transactions = self._create_online_transactions([ - '2024-01-01', '2024-01-02', '2024-01-03', '2024-01-04', '2024-01-05' - ]) - bsls = self.BankStatementLine._online_sync_bank_statement(transactions, self.account_online_account) - - group_1, group_2 = [], [] - for bsl in bsls: - # have to update the online_transaction_identifier after to force duplicates - if bsl.payment_ref in ('transaction_1', 'transaction_2'): - group_1.append(bsl.id) - bsl.online_transaction_identifier = 'same_oti_1' - if bsl.payment_ref in ('transaction_3, transaction_4, transaction_5'): - group_2.append(bsl.id) - bsl.online_transaction_identifier = 'same_oti_2' - - self.env.flush_all() # _get_duplicate_transactions make sql request, must write to db - duplicate_transactions = self.euro_bank_journal._get_duplicate_transactions( - fields.Date.to_date('2000-01-01') - ) - self.assertEqual(duplicate_transactions, [group_1, group_2]) - - @patch('odoo.addons.odex30_account_online_sync.models.account_online.requests') - def test_fetch_receive_error_message(self, patched_request): - # We want to test that when we receive an error, a redirectWarning with the correct parameter is thrown - # However the method _log_information that we need to test for that is performing a rollback as it needs - # to save the message error on the record as well (so it rollback, save message, commit, raise error). - # So in order to test the method, we need to use a "test cursor". - mock_response = MagicMock() - mock_response.status_code = 200 - mock_response.json.return_value = { - 'error': { - 'code': 400, - 'message': 'Shit Happened', - 'data': { - 'exception_type': 'random', - 'message': 'This kind of things can happen.', - 'error_reference': 'abc123', - 'provider_type': 'theonlyone', - 'redirect_warning_url': 'odoo_support', - }, - }, - } - patched_request.post.return_value = mock_response - - generated_url = 'https://www.odoo.com/help?stage=bank_sync&summary=Bank+sync+error+ref%3A+abc123+-+Provider%3A+theonlyone+-+Client+ID%3A+client_id_1&description=ClientID%3A+client_id_1%0AInstitution%3A+Test+Bank%0AError+Reference%3A+abc123%0AError+Message%3A+This+kind+of+things+can+happen.%0A' - return_act_url = { - 'type': 'ir.actions.act_url', - 'url': generated_url - } - body_generated_url = generated_url.replace('&', '&') #in post_message, & has been escaped to & - message_body = f"""

This kind of things can happen. - -If you've already opened a ticket for this issue, don't report it again: a support agent will contact you shortly.
You can contact Odoo support Here

""" - - # flush and clear everything for the new "transaction" - self.env.invalidate_all() - try: - self.env.registry.enter_test_mode(self.cr) - with self.env.registry.cursor() as test_cr: - test_env = self.env(cr=test_cr) - test_link_account = self.account_online_link.with_env(test_env) - test_link_account.state = 'connected' - - # this hand-written self.assertRaises() does not roll back self.cr, - # which is necessary below to inspect the message being posted - try: - test_link_account._fetch_odoo_fin('/testthisurl') - except RedirectWarning as exception: - self.assertEqual(exception.args[0], "This kind of things can happen.\n\nIf you've already opened a ticket for this issue, don't report it again: a support agent will contact you shortly.") - self.assertEqual(exception.args[1], return_act_url) - self.assertEqual(exception.args[2], 'Report issue') - else: - self.fail("Expected RedirectWarning not raised") - self.assertEqual(test_link_account.message_ids[0].body, message_body) - finally: - self.env.registry.leave_test_mode() - - def test_account_online_link_having_journal_ids(self): - """ This test verifies that the account online link object - has all the journal in the field journal_ids. - It's important to handle these journals because we need - them to add the consent expiring date. - """ - # Create a bank sync connection having 2 online accounts (with one journal connected for each account) - online_link = self.env['account.online.link'].create({ - 'name': 'My New Bank connection', - }) - online_accounts = self.env['account.online.account'].create([ - { - 'name': 'Account 1', - 'account_online_link_id': online_link.id, - 'journal_ids': [Command.create({ - 'name': 'Account 1', - 'code': 'BK1', - 'type': 'bank', - })], - }, - { - 'name': 'Account 2', - 'account_online_link_id': online_link.id, - 'journal_ids': [Command.create({ - 'name': 'Account 2', - 'code': 'BK2', - 'type': 'bank', - })], - }, - ]) - self.assertEqual(online_link.account_online_account_ids, online_accounts) - self.assertEqual(len(online_link.journal_ids), 2) # Our online link connections should have 2 journals. - - def test_transaction_details_json_compatibility_from_html(self): - """ This test checks that, after being imported from the transient model - the records of account.bank.statement.line will have the - 'transaction_details' field able to be decoded to a JSON, - i.e. it is not encapsulated in

tags. - """ - transaction = self._create_one_online_transaction() - transaction['transaction_details'] = '{\n "account_id": "1",\n "status": "posted"\n}' - transient_transaction = self.env['account.bank.statement.line.transient'].create(transaction) - transaction_details = transient_transaction.read(fields=['transaction_details'], load=None)[0]['transaction_details'] - self.assertFalse(transaction_details.startswith('

'), 'Transient transaction details should not start with

when read.') - self.assertFalse(transaction_details.endswith('

'), 'Transient transaction details should not end with

when read.') diff --git a/dev_odex30_accounting/odex30_account_online_sync/views/account_bank_statement_view.xml b/dev_odex30_accounting/odex30_account_online_sync/views/account_bank_statement_view.xml deleted file mode 100644 index 678dc78..0000000 --- a/dev_odex30_accounting/odex30_account_online_sync/views/account_bank_statement_view.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - bank.statement.line.list.inherit - account.bank.statement.line - - - - - - - - - - - - account.bank.statement.line.form.bank_rec_widget.inherit - account.bank.statement.line - primary - - - - - - - diff --git a/dev_odex30_accounting/odex30_account_online_sync/views/account_journal_dashboard_view.xml b/dev_odex30_accounting/odex30_account_online_sync/views/account_journal_dashboard_view.xml deleted file mode 100644 index ebe51e5..0000000 --- a/dev_odex30_accounting/odex30_account_online_sync/views/account_journal_dashboard_view.xml +++ /dev/null @@ -1,86 +0,0 @@ - - - - - account.journal.dashboard.inherit.online.sync - account.journal - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - -
-
- - - - - - - account.group_account_manager - - dashboard.display_connect_bank_in_dashboard ? 'col-4' : 'col-6' - - - - account.group_account_manager - - dashboard.display_connect_bank_in_dashboard ? 'col-4' : 'col-6' - - - - - - - - - - - - - - - - - -
-
-
-
diff --git a/dev_odex30_accounting/odex30_account_online_sync/views/account_journal_view.xml b/dev_odex30_accounting/odex30_account_online_sync/views/account_journal_view.xml deleted file mode 100644 index 4e356f0..0000000 --- a/dev_odex30_accounting/odex30_account_online_sync/views/account_journal_view.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - account.journal.form.online.sync - account.journal - - - - - - -
- - - - - - diff --git a/dev_odex30_accounting/odex30_account_online_sync/views/account_online_sync_portal_templates.xml b/dev_odex30_accounting/odex30_account_online_sync/views/account_online_sync_portal_templates.xml deleted file mode 100644 index c0cd687..0000000 --- a/dev_odex30_accounting/odex30_account_online_sync/views/account_online_sync_portal_templates.xml +++ /dev/null @@ -1,59 +0,0 @@ - - - - diff --git a/dev_odex30_accounting/odex30_account_online_sync/views/account_online_sync_views.xml b/dev_odex30_accounting/odex30_account_online_sync/views/account_online_sync_views.xml deleted file mode 100644 index e43ecef..0000000 --- a/dev_odex30_accounting/odex30_account_online_sync/views/account_online_sync_views.xml +++ /dev/null @@ -1,126 +0,0 @@ - - - - - account.online.link.form - account.online.link - -
-
-
- -
-

-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
diff --git a/dev_odex30_accounting/odex30_account_online_sync/wizard/account_journal_missing_transactions.py b/dev_odex30_accounting/odex30_account_online_sync/wizard/account_journal_missing_transactions.py deleted file mode 100644 index f6e0c2c..0000000 --- a/dev_odex30_accounting/odex30_account_online_sync/wizard/account_journal_missing_transactions.py +++ /dev/null @@ -1,78 +0,0 @@ -# Part of Odoo. See LICENSE file for full copyright and licensing details. - -from dateutil.relativedelta import relativedelta - -from odoo import fields, models, _ -from odoo.exceptions import UserError -from odoo.tools import format_date - - -class AccountMissingTransaction(models.TransientModel): - _name = 'account.missing.transaction.wizard' - _description = 'Wizard for missing transactions' - - date = fields.Date( - string="Starting Date", - default=lambda self: fields.Date.today() - relativedelta(months=1), - ) - journal_id = fields.Many2one( - comodel_name='account.journal', - domain="[('type', '=', 'bank'), ('account_online_account_id', '!=', 'False'), ('account_online_link_state', '=', 'connected')]" - ) - - def _get_manual_bank_statement_lines(self): - return self.env['account.bank.statement.line'].search( - domain=[ - ('date', '>=', self.date), - ('journal_id', '=', self.journal_id.id), - ('online_transaction_identifier', '=', False), - ], - ) - - def action_fetch_missing_transaction(self): - self.ensure_one() - - if not self.journal_id: - raise UserError(_("You have to select one journal to continue.")) - - if not self.date: - raise UserError(_("Please enter a valid Starting Date to continue.")) - - if self.journal_id.account_online_link_state != 'connected': - raise UserError(_("You can't find missing transactions for a journal that isn't connected.")) - - fetched_transactions = self.journal_id.account_online_account_id._retrieve_transactions(date=self.date, include_pendings=True) - transactions = fetched_transactions.get('transactions') or [] - pendings = fetched_transactions.get('pendings') or [] - - pendings = [{**pending, 'state': 'pending'} for pending in pendings] - filtered_transactions = self.journal_id.account_online_account_id._get_filtered_transactions(transactions + pendings) - - transient_transactions_ids = self.env['account.bank.statement.line.transient'].create(filtered_transactions) - - return { - 'name': _("Missing and Pending Transactions"), - 'type': 'ir.actions.act_window', - 'res_model': 'account.bank.statement.line.transient', - 'view_mode': 'list', - 'views': [(False, 'list')], - 'domain': [('id', 'in', transient_transactions_ids.ids)], - 'context': { - 'has_manual_entries': bool(self._get_manual_bank_statement_lines()), - 'is_fetch_before_creation': self.date < self.journal_id.account_online_link_id.create_date.date(), - 'account_online_link_create_date': format_date(self.env, self.journal_id.account_online_link_id.create_date), - 'search_default_filter_posted': bool([transaction for transaction in filtered_transactions if transaction.get('state') != 'pending']), # Activate this default filter only if we have posted transactions - }, - } - - def action_open_manual_bank_statement_lines(self): - self.ensure_one() - bank_statement_lines = self._get_manual_bank_statement_lines() - - return { - 'name': _("Manual Bank Statement Lines"), - 'type': 'ir.actions.act_window', - 'res_model': 'account.bank.statement.line', - 'views': [(False, 'list'), (False, 'form')], - 'domain': [('id', 'in', bank_statement_lines.ids)], - } diff --git a/dev_odex30_accounting/odex30_account_online_sync/wizard/account_journal_missing_transactions.xml b/dev_odex30_accounting/odex30_account_online_sync/wizard/account_journal_missing_transactions.xml deleted file mode 100644 index 5d85de2..0000000 --- a/dev_odex30_accounting/odex30_account_online_sync/wizard/account_journal_missing_transactions.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - account.missing.transaction.wizard.form - account.missing.transaction.wizard - -
-

- Choose a date and a journal from which you want to fetch transactions -

- - - - - -
-
-
-
diff --git a/dev_odex30_accounting/odex30_account_reports/__manifest__.py b/dev_odex30_accounting/odex30_account_reports/__manifest__.py index 881f1d9..1cba83b 100644 --- a/dev_odex30_accounting/odex30_account_reports/__manifest__.py +++ b/dev_odex30_accounting/odex30_account_reports/__manifest__.py @@ -10,7 +10,7 @@ Accounting Reports ================== """, - 'depends': ['account'], + 'depends': ['accountant'], 'data': [ 'security/ir.model.access.csv', 'data/pdf_export_templates.xml', @@ -73,10 +73,7 @@ Accounting Reports ], 'web.assets_backend': [ - 'odex30_account_reports/static/src/components/account_report/account_report.xml', # أضف هذا أولاً - 'odex30_account_reports/static/src/components/account_report/account_report.js', - 'odex30_account_reports/static/src/components/**/*.xml', # أضف هذا - 'odex30_account_reports/static/src/components/**/*.js', + 'odex30_account_reports/static/src/components/**/*', 'odex30_account_reports/static/src/js/**/*', 'odex30_account_reports/static/src/widgets/**/*', ],