Compare commits

...

67 Commits

Author SHA1 Message Date
Florian da Costa
b4efcdb2d7 [FIX] commission module installable 2025-11-13 15:46:58 +01:00
Florian da Costa
4b5385f391 [IMP] Add default agent boolean when creating an agent from the commission profile view 2025-11-13 11:29:33 +01:00
Florian da Costa
2f712cb05d [FIX] chatter in commission result view + add action name for all commission related new actions 2025-11-13 11:29:24 +01:00
Florian da Costa
ee3546c2c1 [MIG] commission_simple_agent_purchase to 18 2025-11-13 11:29:20 +01:00
Benoît
7041ae1274 fix missing import 2025-11-13 11:29:13 +01:00
Benoît
6960d52220 mig report 2025-11-13 11:28:57 +01:00
Florian da Costa
faea568a6d [MIG] commission_simple_agent : technical migration to 18 2025-11-13 11:28:51 +01:00
Florian da Costa
e0e3b601e5 [MIG] commission_simple to 18 2025-11-13 11:28:44 +01:00
Florian da Costa
5bd5f117e2 [MIG] sale_agent to v18 2025-11-13 11:28:38 +01:00
Alexis de Lattre
9c7505110b [IMP] commission_simple: allow removal of commission lines 2025-11-13 11:28:13 +01:00
Alexis de Lattre
e39edc14c3 [IMP] commission_simple: allow to restart commission computation on the same period without deleting all commission results 2025-11-13 11:28:06 +01:00
Alexis de Lattre
ffcaff5ab0 [IMP] commission_simple: add index=True on the M2O of account.move.line
This speeds-up the opening of commission results
2025-11-13 11:27:55 +01:00
Alexis de Lattre
b146d14485 [IMP] commission_simple: add commission_amount to account.invoice.report 2025-11-13 11:27:38 +01:00
Alexis de Lattre
635078f205 [IMP] commission_simple: fine-tune access rights 2025-11-13 11:24:03 +01:00
Alexis de Lattre
fadabd66b8 [IMP] commission_simple: periodicity moved from company to profile
Update commission_simple_agent_purchase to adapt config page form view
accordingly
2025-11-13 11:23:56 +01:00
Alexis de Lattre
988d63fac0 [IMP] commission_simple: validation from list view 2025-11-13 11:23:50 +01:00
Alexis de Lattre
b02ff59054 [IMP] commission_simple: add total base amount in view and XLSX report 2025-11-13 11:23:45 +01:00
Alexis de Lattre
7531143b45 [IMP] commission_simple: update fr translation 2025-11-13 11:23:36 +01:00
Alexis de Lattre
c530ea0e9a [IMP] commission_simple: allow use of lambda in inherit of commission result lines 2025-11-13 11:23:28 +01:00
Alexis de Lattre
1060dd60f6 [IMP] commission_simple: XSLX report sorted by date (inheritable) 2025-11-13 11:23:16 +01:00
Alexis de Lattre
232cb24fd9 [IMP] commission_simple*: add XLSX report + improve PO generation
Add message un chatter of PO
2025-11-13 11:23:09 +01:00
Alexis de Lattre
827119df6e [IMP] commission_simple: improve views and add description for rule list views 2025-11-13 11:23:02 +01:00
Alexis de Lattre
a9a2ee5ec4 [IMP] agent: headers 2025-11-13 11:22:55 +01:00
Alexis de Lattre
f296a3edb8 [IMP] commission_simple: code cleanup and minor improvements 2025-11-13 11:22:47 +01:00
Alexis de Lattre
ecb7262e5c [IMP] account_usability_akretion: add fiscal position in partner list view (optional hide) 2025-11-07 17:48:42 +00:00
Akretion Git Bot
8cd07c93e9 [BOT] add or update setup/_metapackage 2025-11-07 17:44:05 +00:00
Akretion Git Bot
e01d2440ac [BOT] add pyproject.toml 2025-11-07 17:44:05 +00:00
Alexis de Lattre
940c13b449 [MIG] crm_usability_akretion from 16 to 18 2025-11-07 17:43:39 +00:00
Alexis de Lattre
1cea91eeaa [FIX] account_usability_akretion: double Print button on invoice 2025-11-07 17:43:13 +00:00
Akretion Git Bot
2a008a303c [BOT] add or update setup/_metapackage 2025-10-15 12:05:56 +00:00
Akretion Git Bot
743c2dad84 [BOT] add pyproject.toml 2025-10-15 12:05:56 +00:00
Akretion Git Bot
d47c62dbed [ADD] icon.png 2025-10-15 12:05:55 +00:00
Florian
c5c3319963 Merge pull request #232 from akretion/18-mig-account_invoice_update_wizard
[18][MIG] account_invoice_update_wizard
2025-10-15 14:05:50 +02:00
Alexis de Lattre
8df73ed96a purchase_usability_akretion: Revert my previous commit
The field has_discount has no business interest on purchase.order reports.
2025-10-07 21:18:41 +02:00
Alexis de Lattre
e89c1a0f9c [IMP] purchase_usability_akretion: add has_discount field on purchase.order (used in reports) 2025-10-07 19:31:04 +02:00
Alexis de Lattre
29678e2de3 [UDP] l10n_fr_account_profile_akretion: update deps
Remove dep on l10n_fr_intrastat_product because it depends on sale/purchase/stock
2025-10-02 18:29:17 +02:00
Akretion Git Bot
54c6eac6fd [BOT] add or update setup/_metapackage 2025-10-02 16:27:17 +00:00
Akretion Git Bot
32814194df [BOT] add pyproject.toml 2025-10-02 16:27:17 +00:00
Akretion Git Bot
7126cfee72 [ADD] icon.png 2025-10-02 16:27:17 +00:00
Alexis de Lattre
a78cf95b43 [MIG] base_mail_sender_bcc to v18 2025-10-02 18:26:58 +02:00
Alexis de Lattre
cefcb1b5c2 [UDP] l10n_fr_account_profile_akretion: Add dep on account_payment_batch_oca_reconcile in comment 2025-09-05 11:56:40 +02:00
Alexis de Lattre
36b35f1b87 [IMP] l10n_fr_account_profile_akretion: Add dep on account_dashboard_banner 2025-07-17 15:28:24 +02:00
Alexis de Lattre
f1f808d977 [UDP] l10n_fr_account_profile_akretion: new module names and newly ported modules 2025-06-25 16:02:24 +02:00
Alexis de Lattre
11f3d393b5 mail_debrand has been moved from OCA/social to OCA/mail in v18 2025-06-25 12:31:18 +02:00
Alexis de Lattre
ca5730c701 [IMP] base_profile_akretion: add mail_debrand 2025-06-25 12:23:13 +02:00
Alexis de Lattre
76a1bbd9f4 [IMP] stock_usability_akretion: add button to show reservations from quand tree view 2025-06-23 12:32:03 +02:00
Alexis de Lattre
48d5a3b327 [IMP] stock_quant_package_move_wizard: more accurate source location on picking when all quants have the same location 2025-06-23 12:24:43 +02:00
Akretion Git Bot
836a88dfeb [BOT] add or update setup/_metapackage 2025-05-26 10:16:44 +00:00
Akretion Git Bot
8182b839d4 [BOT] add pyproject.toml 2025-05-26 10:16:43 +00:00
Akretion Git Bot
6cd86f8820 [ADD] icon.png 2025-05-26 10:16:43 +00:00
Alexis de Lattre
76e93f0379 [MIG] l10n_fr_account_profile_akretion from v16 to v18 2025-05-26 12:16:18 +02:00
Alexis de Lattre
d9f65b96fc Remove patch web-advanced_search_startswith.diff (native in v18) 2025-05-26 11:47:26 +02:00
Akretion Git Bot
a729762b4a [BOT] add or update setup/_metapackage 2025-05-26 09:19:03 +00:00
Akretion Git Bot
18551f9b0d [BOT] add pyproject.toml 2025-05-26 09:19:03 +00:00
Akretion Git Bot
05b2d14cd2 [ADD] icon.png 2025-05-26 09:19:02 +00:00
Akretion Git Bot
1d57615b97 [UPD] README.rst 2025-05-26 09:19:02 +00:00
Alexis de Lattre
545cd797e3 [MIG] base_company_extension, eradicate_quick_create, mail_usability_akretion, base_profile_akretion to v18 2025-05-26 11:18:22 +02:00
Alexis de Lattre
70b66681d6 [IMP] stock_usability_akretion: add seller_id on orderpoints 2025-05-20 10:30:29 +02:00
Akretion Git Bot
2e93aaa55b [UPD] README.rst 2025-04-01 16:35:45 +00:00
Alexis de Lattre
5b6a166ed6 [IMP] account_usability_akretion: add move_id on account.payment form view 2025-04-01 18:35:07 +02:00
Florian da Costa
a76cb62bea [MIG] account_invoice_update_wizard to 18 2025-03-21 17:01:38 +01:00
Florian da Costa
03c3f8c15d [FIX] account_invoice_update_wizard : avoid stacktrace when no pdf is found 2025-03-21 16:21:16 +01:00
Sébastien BEAU
6d855f4397 account_invoice_update_wizard: purge existing pdf in attachment after update 2025-03-21 16:21:03 +01:00
Florian da Costa
9fc47065ff [IMP] Add bill date (for supplier invoice) and rename bill ref to supplier bill ref to be consistent with name in invoice 2025-03-21 16:20:47 +01:00
Florian da Costa
89f9a2afa3 [IMP] account_invoice_update_wizard : rename button for accounting entries + hide invoice specific fields in case of accountring entries 2025-03-21 16:20:38 +01:00
Sébastien BEAU
6ff99bd91f account_invoice_update_wizard: inactive update of broken payment term
We should add test and fix it
2025-03-21 16:20:25 +01:00
Sébastien BEAU
66f72145d5 account_invoice_update_wizard: fix UI add translation
Add section support and keep order based on the line sequence
Add Fr translation
2025-03-21 16:20:15 +01:00
93 changed files with 1567 additions and 760 deletions

View File

@@ -4,7 +4,7 @@
{ {
'name': 'Account Invoice Update Wizard', 'name': 'Account Invoice Update Wizard',
'version': '14.0.1.0.0', 'version': '18.0.1.0.0',
'category': 'Accounting & Finance', 'category': 'Accounting & Finance',
'license': 'AGPL-3', 'license': 'AGPL-3',
'summary': 'Wizard to update non-legal fields of an open/paid invoice', 'summary': 'Wizard to update non-legal fields of an open/paid invoice',
@@ -18,5 +18,5 @@
'wizard/account_move_update_view.xml', 'wizard/account_move_update_view.xml',
'views/account_move.xml', 'views/account_move.xml',
], ],
'installable': False, 'installable': True,
} }

View File

@@ -0,0 +1,243 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * account_invoice_update_wizard
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 14.0\n"
"Report-Msgid-Bugs-To: \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_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_line_update__price_subtotal
msgid "Amount"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_line_update__analytic_account_id
msgid "Analytic Account"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_line_update__analytic_tag_ids
msgid "Analytic Tags"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_update__partner_bank_id
msgid "Bank Account"
msgstr ""
#. module: account_invoice_update_wizard
#: model_terms:ir.ui.view,arch_db:account_invoice_update_wizard.account_invoice_update_form
msgid "Bill Reference"
msgstr ""
#. module: account_invoice_update_wizard
#: model_terms:ir.ui.view,arch_db:account_invoice_update_wizard.account_invoice_update_form
msgid "Cancel"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_update__company_id
msgid "Company"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_line_update__create_uid
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_update__create_uid
msgid "Created by"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_line_update__create_date
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_update__create_date
msgid "Created on"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_line_update__currency_id
msgid "Currency"
msgstr ""
#. module: account_invoice_update_wizard
#: model_terms:ir.ui.view,arch_db:account_invoice_update_wizard.account_invoice_update_form
msgid "Customer Reference"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_line_update__name
msgid "Description"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move__display_name
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_line_update__display_name
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_update__display_name
msgid "Display Name"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_line_update__display_type
msgid "Display Type"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move__id
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_line_update__id
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_update__id
msgid "ID"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_update__invoice_id
msgid "Invoice"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_line_update__invoice_line_id
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_update__line_ids
msgid "Invoice Lines"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.actions.act_window,name:account_invoice_update_wizard.account_invoice_update_action
msgid "Invoice Update Wizard"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model,name:account_invoice_update_wizard.model_account_move
msgid "Journal Entry"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move____last_update
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_line_update____last_update
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_update____last_update
msgid "Last Modified on"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_line_update__write_uid
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_update__write_uid
msgid "Last Updated by"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_line_update__write_date
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_update__write_date
msgid "Last Updated on"
msgstr ""
#. module: account_invoice_update_wizard
#: code:addons/account_invoice_update_wizard/wizard/account_move_update.py:0
#, python-format
msgid "Non-legal fields of invoice updated via the Invoice Update wizard."
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model.fields.selection,name:account_invoice_update_wizard.selection__account_move_line_update__display_type__line_note
msgid "Note"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_update__partner_id
msgid "Partner"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_update__invoice_payment_term_id
msgid "Payment Term"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_line_update__quantity
msgid "Quantity"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_update__ref
msgid "Reference"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_update__user_id
msgid "Salesperson"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model.fields.selection,name:account_invoice_update_wizard.selection__account_move_line_update__display_type__line_section
msgid "Section"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_line_update__sequence
msgid "Sequence"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_update__invoice_origin
msgid "Source Document"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model.fields,help:account_invoice_update_wizard.field_account_move_line_update__display_type
msgid "Technical field for UX purpose."
msgstr ""
#. module: account_invoice_update_wizard
#: code:addons/account_invoice_update_wizard/wizard/account_move_update.py:0
#, python-format
msgid ""
"The original payment term '%s' doesn't have the same terms (number of terms "
"and/or amount) as the new payment term '%s'. You can only switch to a "
"payment term that has the same number of terms with the same amount."
msgstr ""
#. module: account_invoice_update_wizard
#: code:addons/account_invoice_update_wizard/wizard/account_move_update.py:0
#, python-format
msgid ""
"This wizard doesn't support the update of payment terms on an invoice which "
"is partially or fully paid."
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_update__move_type
msgid "Type"
msgstr ""
#. module: account_invoice_update_wizard
#: model_terms:ir.ui.view,arch_db:account_invoice_update_wizard.account_invoice_update_form
msgid "Update"
msgstr ""
#. module: account_invoice_update_wizard
#: model_terms:ir.ui.view,arch_db:account_invoice_update_wizard.view_move_form_inherit
msgid "Update Invoice"
msgstr ""
#. module: account_invoice_update_wizard
#: model_terms:ir.ui.view,arch_db:account_invoice_update_wizard.account_invoice_update_form
msgid "Update Invoice Wizard"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model,name:account_invoice_update_wizard.model_account_move_line_update
msgid "Update non-legal fields of invoice lines"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_line_update__parent_id
msgid "Wizard"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model,name:account_invoice_update_wizard.model_account_move_update
msgid "Wizard to update non-legal fields of invoice"
msgstr ""

View File

@@ -0,0 +1,250 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * account_invoice_update_wizard
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 14.0\n"
"Report-Msgid-Bugs-To: \n"
"Last-Translator: \n"
"Language-Team: \n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_line_update__price_subtotal
msgid "Amount"
msgstr "Montant"
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_line_update__analytic_account_id
msgid "Analytic Account"
msgstr "Compte Analytique"
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_line_update__analytic_tag_ids
msgid "Analytic Tags"
msgstr "Tag Analytique"
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_update__partner_bank_id
msgid "Bank Account"
msgstr "Compte Bancaire"
#. module: account_invoice_update_wizard
#: model_terms:ir.ui.view,arch_db:account_invoice_update_wizard.account_invoice_update_form
#, fuzzy
msgid "Bill Reference"
msgstr "Reference Client"
#. module: account_invoice_update_wizard
#: model_terms:ir.ui.view,arch_db:account_invoice_update_wizard.account_invoice_update_form
msgid "Cancel"
msgstr "Annuler"
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_update__company_id
msgid "Company"
msgstr "Société"
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_line_update__create_uid
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_update__create_uid
msgid "Created by"
msgstr "Créé par"
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_line_update__create_date
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_update__create_date
msgid "Created on"
msgstr "Créé le"
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_line_update__currency_id
msgid "Currency"
msgstr "Devise"
#. module: account_invoice_update_wizard
#: model_terms:ir.ui.view,arch_db:account_invoice_update_wizard.account_invoice_update_form
#, fuzzy
msgid "Customer Reference"
msgstr "Reference Client"
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_line_update__name
msgid "Description"
msgstr "Description"
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move__display_name
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_line_update__display_name
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_update__display_name
msgid "Display Name"
msgstr "Nom"
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_line_update__display_type
msgid "Display Type"
msgstr "Type Affichage"
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move__id
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_line_update__id
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_update__id
msgid "ID"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_update__invoice_id
msgid "Invoice"
msgstr "Facture"
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_line_update__invoice_line_id
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_update__line_ids
msgid "Invoice Lines"
msgstr "Ligne de factures"
#. module: account_invoice_update_wizard
#: model:ir.actions.act_window,name:account_invoice_update_wizard.account_invoice_update_action
msgid "Invoice Update Wizard"
msgstr "Assistance de mise à jour de la facture"
#. module: account_invoice_update_wizard
#: model:ir.model,name:account_invoice_update_wizard.model_account_move
msgid "Journal Entry"
msgstr "Entrée comptable"
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move____last_update
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_line_update____last_update
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_update____last_update
msgid "Last Modified on"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_line_update__write_uid
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_update__write_uid
msgid "Last Updated by"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_line_update__write_date
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_update__write_date
msgid "Last Updated on"
msgstr ""
#. module: account_invoice_update_wizard
#: code:addons/account_invoice_update_wizard/wizard/account_move_update.py:0
#, python-format
msgid "Non-legal fields of invoice updated via the Invoice Update wizard."
msgstr "Champs non légaux mis à jour via l'assistant"
#. module: account_invoice_update_wizard
#: model:ir.model.fields.selection,name:account_invoice_update_wizard.selection__account_move_line_update__display_type__line_note
msgid "Note"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_update__partner_id
msgid "Partner"
msgstr "Client"
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_update__invoice_payment_term_id
msgid "Payment Term"
msgstr "Condition de paiement"
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_line_update__quantity
msgid "Quantity"
msgstr "Quantité"
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_update__ref
#, fuzzy
msgid "Reference"
msgstr "Reference Client"
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_update__user_id
msgid "Salesperson"
msgstr "Vendeur"
#. module: account_invoice_update_wizard
#: model:ir.model.fields.selection,name:account_invoice_update_wizard.selection__account_move_line_update__display_type__line_section
msgid "Section"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_line_update__sequence
msgid "Sequence"
msgstr "Sequence"
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_update__invoice_origin
msgid "Source Document"
msgstr "Origine du document"
#. module: account_invoice_update_wizard
#: model:ir.model.fields,help:account_invoice_update_wizard.field_account_move_line_update__display_type
msgid "Technical field for UX purpose."
msgstr ""
#. module: account_invoice_update_wizard
#: code:addons/account_invoice_update_wizard/wizard/account_move_update.py:0
#, python-format
msgid ""
"The original payment term '%s' doesn't have the same terms (number of terms "
"and/or amount) as the new payment term '%s'. You can only switch to a "
"payment term that has the same number of terms with the same amount."
msgstr ""
#. module: account_invoice_update_wizard
#: code:addons/account_invoice_update_wizard/wizard/account_move_update.py:0
#, python-format
msgid ""
"This wizard doesn't support the update of payment terms on an invoice which "
"is partially or fully paid."
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_update__move_type
msgid "Type"
msgstr ""
#. module: account_invoice_update_wizard
#: model_terms:ir.ui.view,arch_db:account_invoice_update_wizard.account_invoice_update_form
msgid "Update"
msgstr "Mettre à jour"
#. module: account_invoice_update_wizard
#: model_terms:ir.ui.view,arch_db:account_invoice_update_wizard.view_move_form_inherit
msgid "Update Invoice"
msgstr "Mettre à jour"
#. module: account_invoice_update_wizard
#: model_terms:ir.ui.view,arch_db:account_invoice_update_wizard.account_invoice_update_form
msgid "Update Invoice Wizard"
msgstr "Assistant de mise à jour"
#. module: account_invoice_update_wizard
#: model:ir.model,name:account_invoice_update_wizard.model_account_move_line_update
msgid "Update non-legal fields of invoice lines"
msgstr "Mettre à jour les champs non légaux des lignes de facture"
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_line_update__parent_id
msgid "Wizard"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model,name:account_invoice_update_wizard.model_account_move_update
msgid "Wizard to update non-legal fields of invoice"
msgstr "Assistant pour mettre à jours les champs non légaux"
#~ msgid "Account"
#~ msgstr "Compte"

View File

@@ -0,0 +1,3 @@
[build-system]
requires = ["whool"]
build-backend = "whool.buildapi"

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

View File

@@ -1,10 +1,10 @@
# Copyright 2018-2022 Camptocamp # Copyright 2018-2022 Camptocamp
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo.tests.common import SavepointCase from odoo.tests.common import TransactionCase
class TestAccountInvoiceUpdateWizard(SavepointCase): class TestAccountInvoiceUpdateWizard(TransactionCase):
@classmethod @classmethod
def setUpClass(cls): def setUpClass(cls):
@@ -13,6 +13,17 @@ class TestAccountInvoiceUpdateWizard(SavepointCase):
cls.product16 = cls.env.ref('product.product_product_16') cls.product16 = cls.env.ref('product.product_product_16')
uom_unit = cls.env.ref('uom.product_uom_categ_unit') uom_unit = cls.env.ref('uom.product_uom_categ_unit')
cls.plan = cls.env['account.analytic.plan'].create({'name': 'Test Plan'})
cls.analytic_account_1 = cls.env['account.analytic.account'].create({
'name': 'analytic 1 test plan',
'plan_id': cls.plan.id,
'company_id': False,
})
cls.analytic_account_2 = cls.env['account.analytic.account'].create({
'name': 'analytic 2 test plan',
'plan_id': cls.plan.id,
'company_id': False,
})
cls.move1 = cls.env['account.move'].create({ cls.move1 = cls.env['account.move'].create({
'name': 'Test invoice', 'name': 'Test invoice',
'partner_id': cls.customer12.id, 'partner_id': cls.customer12.id,
@@ -30,13 +41,6 @@ class TestAccountInvoiceUpdateWizard(SavepointCase):
], ],
}) })
cls.aa1 = cls.env.ref('analytic.analytic_partners_camp_to_camp')
cls.aa2 = cls.env.ref('analytic.analytic_nebula')
cls.atag1 = cls.env.ref('analytic.tag_contract')
cls.atag2 = cls.env['account.analytic.tag'].create({
'name': '',
})
def create_wizard(self, move): def create_wizard(self, move):
res = move.prepare_update_wizard() res = move.prepare_update_wizard()
self.wiz = self.env['account.move.update'].browse(res['res_id']) self.wiz = self.env['account.move.update'].browse(res['res_id'])
@@ -54,13 +58,14 @@ class TestAccountInvoiceUpdateWizard(SavepointCase):
wiz_line = self.wiz.line_ids.filtered( wiz_line = self.wiz.line_ids.filtered(
lambda rec: rec.invoice_line_id.product_id.id == self.product16.id) lambda rec: rec.invoice_line_id.product_id.id == self.product16.id)
wiz_line.analytic_account_id = self.aa1 wiz_line.analytic_distribution = {self.analytic_account_1.id: 50, self.analytic_account_2.id: 50}
self.wiz.run() self.wiz.run()
related_ml = self.move1.invoice_line_ids.filtered( related_ml = self.move1.invoice_line_ids.filtered(
lambda rec: rec.product_id == self.product16) lambda rec: rec.product_id == self.product16)
self.assertEqual(related_ml.analytic_account_id, self.aa1) self.assertEqual(related_ml.analytic_distribution, {str(self.analytic_account_1.id): 50.0, str(self.analytic_account_2.id): 50.0})
self.assertEqual(related_ml.analytic_line_ids.account_id, self.aa1) self.assertEqual(len(related_ml.analytic_line_ids), 2)
self.assertEqual(related_ml.analytic_line_ids[0].amount, 21.0)
def test_change_analytic_account_line1(self): def test_change_analytic_account_line1(self):
""" Change analytic account on a move line """ Change analytic account on a move line
@@ -70,86 +75,21 @@ class TestAccountInvoiceUpdateWizard(SavepointCase):
- update the move line - update the move line
- update the existing analytic line.""" - update the existing analytic line."""
move_line1 = self.move1.invoice_line_ids.filtered(lambda rec: rec.product_id == self.product16) move_line1 = self.move1.invoice_line_ids.filtered(lambda rec: rec.product_id == self.product16)
move_line1.analytic_account_id = self.aa2 move_line1.analytic_distribution = {self.analytic_account_1.id: 100}
self.move1._post() self.move1._post()
self.create_wizard(self.move1) self.create_wizard(self.move1)
wiz_line = self.wiz.line_ids.filtered( wiz_line = self.wiz.line_ids.filtered(
lambda rec: rec.invoice_line_id.product_id.id == self.product16.id) lambda rec: rec.invoice_line_id.product_id.id == self.product16.id)
wiz_line.analytic_account_id = self.aa1 wiz_line.analytic_distribution = {self.analytic_account_1.id: 50, self.analytic_account_2.id: 50}
self.wiz.run() self.wiz.run()
related_ml = self.move1.invoice_line_ids.filtered( related_ml = self.move1.invoice_line_ids.filtered(
lambda rec: rec.product_id == self.product16) lambda rec: rec.product_id == self.product16)
self.assertEqual(related_ml.analytic_account_id, self.aa1) self.assertEqual(related_ml.analytic_distribution, {str(self.analytic_account_1.id): 50.0, str(self.analytic_account_2.id): 50.0})
self.assertEqual(related_ml.analytic_line_ids.account_id, self.aa1) self.assertEqual(len(related_ml.analytic_line_ids), 2)
self.assertEqual(related_ml.analytic_line_ids[0].amount, 21.0)
def test_add_analytic_tags_line1(self):
""" Add analytic tags on a move line
after the move has been approved.
This will update move line.
"""
self.move1._post()
self.create_wizard(self.move1)
wiz_line = self.wiz.line_ids.filtered(
lambda rec: rec.invoice_line_id.product_id.id == self.product16.id)
wiz_line.analytic_tag_ids = self.atag2
self.wiz.run()
related_ml = self.move1.invoice_line_ids.filtered(
lambda rec: rec.product_id == self.product16)
self.assertEqual(related_ml.analytic_tag_ids, self.atag2)
self.assertFalse(related_ml.analytic_line_ids)
def test_change_analytic_tags_line1(self):
""" Change analytic tags on a move line
after the move has been approved.
It will update move line and analytic line
"""
move_line1 = self.move1.invoice_line_ids.filtered(lambda rec: rec.product_id == self.product16)
move_line1.analytic_account_id = self.aa2
move_line1.analytic_tag_ids = self.atag1
self.move1._post()
self.create_wizard(self.move1)
wiz_line = self.wiz.line_ids.filtered(
lambda rec: rec.invoice_line_id.product_id.id == self.product16.id)
wiz_line.analytic_tag_ids = self.atag2
self.wiz.run()
related_ml = self.move1.invoice_line_ids.filtered(
lambda rec: rec.product_id == self.product16)
self.assertEqual(related_ml.analytic_tag_ids, self.atag2)
self.assertEqual(related_ml.analytic_line_ids.tag_ids, self.atag2)
def test_add_analytic_info_line1(self):
""" Add analytic account and tags on a move line
after the move has been approved.
This will:
- update move line
- create an analytic line
"""
self.move1._post()
self.create_wizard(self.move1)
wiz_line = self.wiz.line_ids.filtered(
lambda rec: rec.invoice_line_id.product_id.id == self.product16.id)
wiz_line.analytic_account_id = self.aa1
wiz_line.analytic_tag_ids = self.atag2
self.wiz.run()
related_ml = self.move1.invoice_line_ids.filtered(
lambda rec: rec.product_id == self.product16)
self.assertEqual(related_ml.analytic_account_id, self.aa1)
self.assertEqual(related_ml.analytic_tag_ids, self.atag2)
self.assertEqual(related_ml.analytic_line_ids.account_id, self.aa1)
self.assertEqual(related_ml.analytic_line_ids.tag_ids, self.atag2)
def test_empty_analytic_account_line1(self): def test_empty_analytic_account_line1(self):
""" Remove analytic account """ Remove analytic account
@@ -158,16 +98,16 @@ class TestAccountInvoiceUpdateWizard(SavepointCase):
This will raise an error as it is not implemented. This will raise an error as it is not implemented.
""" """
move_line1 = self.move1.invoice_line_ids.filtered(lambda rec: rec.product_id == self.product16) move_line1 = self.move1.invoice_line_ids.filtered(lambda rec: rec.product_id == self.product16)
move_line1.analytic_account_id = self.aa2 move_line1.analytic_distribution = {self.analytic_account_1.id: 100}
self.move1._post() self.move1._post()
self.create_wizard(self.move1) self.create_wizard(self.move1)
wiz_line = self.wiz.line_ids.filtered( wiz_line = self.wiz.line_ids.filtered(
lambda rec: rec.invoice_line_id.product_id.id == self.product16.id) lambda rec: rec.invoice_line_id.product_id.id == self.product16.id)
wiz_line.analytic_account_id = False wiz_line.analytic_distribution = False
self.wiz.run() self.wiz.run()
related_ml = self.move1.invoice_line_ids.filtered( related_ml = self.move1.invoice_line_ids.filtered(
lambda rec: rec.product_id == self.product16) lambda rec: rec.product_id == self.product16)
self.assertFalse(related_ml.analytic_account_id) self.assertFalse(related_ml.analytic_distribution)
self.assertFalse(related_ml.analytic_line_ids) self.assertFalse(related_ml.analytic_line_ids)

View File

@@ -11,7 +11,8 @@
<field name="inherit_id" ref="account.view_move_form"/> <field name="inherit_id" ref="account.view_move_form"/>
<field name="arch" type="xml"> <field name="arch" type="xml">
<button name="button_draft" position="before"> <button name="button_draft" position="before">
<button name="prepare_update_wizard" type="object" string="Update Invoice" states="posted" groups="account.group_account_invoice"/> <button name="prepare_update_wizard" type="object" string="Update Invoice" invisible="state != 'posted' or move_type == 'entry'" groups="account.group_account_invoice"/>
<button name="prepare_update_wizard" type="object" string="Update Entry" invisible="state != 'posted' or move_type != 'entry'" groups="account.group_account_invoice"/>
</button> </button>
</field> </field>
</record> </record>

View File

@@ -2,7 +2,7 @@
# Copyright 2018-2022 Camptocamp # Copyright 2018-2022 Camptocamp
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import models, fields, api, _ from odoo import Command, models, fields, api
from odoo.exceptions import UserError from odoo.exceptions import UserError
import odoo.addons.decimal_precision as dp import odoo.addons.decimal_precision as dp
@@ -17,10 +17,11 @@ class AccountMoveUpdate(models.TransientModel):
move_type = fields.Selection(related='invoice_id.move_type') move_type = fields.Selection(related='invoice_id.move_type')
company_id = fields.Many2one(related='invoice_id.company_id') company_id = fields.Many2one(related='invoice_id.company_id')
partner_id = fields.Many2one(related='invoice_id.partner_id') partner_id = fields.Many2one(related='invoice_id.partner_id')
user_id = fields.Many2one('res.users', string='Salesperson') invoice_user_id = fields.Many2one('res.users', string='Salesperson')
invoice_payment_term_id = fields.Many2one( invoice_payment_term_id = fields.Many2one(
'account.payment.term', string='Payment Term') 'account.payment.term', string='Payment Term')
ref = fields.Char(string='Reference') # field label is customized in the view ref = fields.Char(string='Reference') # field label is customized in the view
invoice_date = fields.Date()
invoice_origin = fields.Char(string='Source Document') invoice_origin = fields.Char(string='Source Document')
partner_bank_id = fields.Many2one( partner_bank_id = fields.Many2one(
'res.partner.bank', string='Bank Account') 'res.partner.bank', string='Bank Account')
@@ -30,11 +31,11 @@ class AccountMoveUpdate(models.TransientModel):
@api.model @api.model
def _simple_fields2update(self): def _simple_fields2update(self):
'''List boolean, date, datetime, char, text fields''' '''List boolean, date, datetime, char, text fields'''
return ['ref', 'invoice_origin'] return ['ref', 'invoice_origin', 'invoice_date']
@api.model @api.model
def _m2o_fields2update(self): def _m2o_fields2update(self):
return ['invoice_payment_term_id', 'user_id', 'partner_bank_id'] return ['invoice_payment_term_id', 'invoice_user_id', 'partner_bank_id']
@api.model @api.model
def _prepare_default_get(self, invoice): def _prepare_default_get(self, invoice):
@@ -44,17 +45,14 @@ class AccountMoveUpdate(models.TransientModel):
for m2ofield in self._m2o_fields2update(): for m2ofield in self._m2o_fields2update():
res[m2ofield] = invoice[m2ofield].id or False res[m2ofield] = invoice[m2ofield].id or False
for line in invoice.invoice_line_ids: for line in invoice.invoice_line_ids:
aa_tags = line.analytic_tag_ids
aa_tags = [(6, 0, aa_tags.ids)] if aa_tags else False
res['line_ids'].append([0, 0, { res['line_ids'].append([0, 0, {
'invoice_line_id': line.id, 'invoice_line_id': line.id,
'sequence': line.sequence,
'name': line.name, 'name': line.name,
'quantity': line.quantity, 'quantity': line.quantity,
'price_subtotal': line.price_subtotal, 'price_subtotal': line.price_subtotal,
'analytic_account_id': line.analytic_account_id.id, 'analytic_distribution': line.analytic_distribution,
'currency_id': line.currency_id.id, 'currency_id': line.currency_id.id,
'analytic_tag_ids': aa_tags,
'display_type': line.display_type,
}]) }])
return res return res
@@ -87,15 +85,15 @@ class AccountMoveUpdate(models.TransientModel):
@api.model @api.model
def _line_simple_fields2update(self): def _line_simple_fields2update(self):
return ["name"] return ["name", "analytic_distribution"]
@api.model @api.model
def _line_m2o_fields2update(self): def _line_m2o_fields2update(self):
return ["analytic_account_id"] return []
@api.model @api.model
def _line_m2m_fields2update(self): def _line_m2m_fields2update(self):
return ["analytic_tag_ids"] return []
@api.model @api.model
def _prepare_invoice_line(self, line): def _prepare_invoice_line(self, line):
@@ -108,30 +106,9 @@ class AccountMoveUpdate(models.TransientModel):
vals[field] = line[field].id vals[field] = line[field].id
for field in self._line_m2m_fields2update(): for field in self._line_m2m_fields2update():
if line[field] != line.invoice_line_id[field]: if line[field] != line.invoice_line_id[field]:
vals[field] = [(6, 0, line[field].ids)] vals[field] = [Command.set(line[field].ids)]
return vals return vals
def _prepare_move_line_and_analytic_line(self, inv_line):
mlvals = {}
alvals = {}
inv_line_upd = self.line_ids.filtered(
lambda rec: rec.invoice_line_id == inv_line)
ini_aa = inv_line.analytic_account_id
new_aa = inv_line_upd.analytic_account_id
if ini_aa != new_aa:
mlvals['analytic_account_id'] = new_aa.id
alvals['account_id'] = new_aa.id
ini_aa_tags = inv_line.analytic_tag_ids
new_aa_tags = inv_line_upd.analytic_tag_ids
if ini_aa_tags != new_aa_tags:
mlvals['analytic_tag_ids'] = [(6, None, new_aa_tags.ids)]
alvals['tag_ids'] = [(6, None, new_aa_tags.ids)]
return mlvals, alvals
def _update_payment_term_move(self): def _update_payment_term_move(self):
self.ensure_one() self.ensure_one()
inv = self.invoice_id inv = self.invoice_id
@@ -144,7 +121,7 @@ class AccountMoveUpdate(models.TransientModel):
# the reconcile marks to put the new maturity date on the right # the reconcile marks to put the new maturity date on the right
# lines # lines
if inv.payment_id: if inv.payment_id:
raise UserError(_( raise UserError(self.env._(
"This wizard doesn't support the update of payment " "This wizard doesn't support the update of payment "
"terms on an invoice which is partially or fully " "terms on an invoice which is partially or fully "
"paid.")) "paid."))
@@ -168,7 +145,7 @@ class AccountMoveUpdate(models.TransientModel):
mlines[amount] = [line] mlines[amount] = [line]
for iamount, lines in mlines.items(): for iamount, lines in mlines.items():
if len(lines) != len(new_pterm.get(iamount, [])): if len(lines) != len(new_pterm.get(iamount, [])):
raise UserError(_( raise UserError(self.env._(
"The original payment term '%s' doesn't have the " "The original payment term '%s' doesn't have the "
"same terms (number of terms and/or amount) as the " "same terms (number of terms and/or amount) as the "
"new payment term '%s'. You can only switch to a " "new payment term '%s'. You can only switch to a "
@@ -188,64 +165,55 @@ class AccountMoveUpdate(models.TransientModel):
if ivals: if ivals:
updated = True updated = True
inv.write(ivals) inv.write(ivals)
if inv:
for ml in inv.line_ids.filtered(
# we are only interested in invoice lines, not tax lines
lambda rec: bool(rec.product_id)
):
if ml.credit == 0.0:
continue
analytic_account = ml.analytic_account_id
mlvals, alvals = self._prepare_move_line_and_analytic_line(ml)
if mlvals:
updated = True
ml.write(mlvals)
aalines = ml.analytic_line_ids
if aalines and alvals:
updated = True
if ('account_id' in alvals and
alvals['account_id'] is False):
former_aa = analytic_account
to_remove_aalines = aalines.filtered(
lambda rec: rec.account_id == former_aa)
# remove existing analytic line
to_remove_aalines.unlink()
else:
aalines.write(alvals)
elif 'account_id' in alvals:
# Create analytic lines if analytic account
# is added later
ml.create_analytic_lines()
for line in self.line_ids: for line in self.line_ids:
ilvals = self._prepare_invoice_line(line) ilvals = self._prepare_invoice_line(line)
if ilvals: if ilvals:
updated = True updated = True
# note that updating analytic_distribution will delete/re-create
# the analytic line with inverse method, we do not need additional
# logic about that.
line.invoice_line_id.write(ilvals) line.invoice_line_id.write(ilvals)
if updated: if updated:
inv.message_post(body=_( inv.message_post(body=self.env._(
'Non-legal fields of invoice updated via the Invoice Update ' 'Non-legal fields of invoice updated via the Invoice Update '
'wizard.')) 'wizard.'))
# Purge existing PDF
report = self.env.ref("account.account_invoices")
attachment = report.retrieve_attachment(inv)
# attachment may be None
if attachment:
attachment.unlink()
return True return True
class AccountMoveLineUpdate(models.TransientModel): class AccountMoveLineUpdate(models.TransientModel):
_name = 'account.move.line.update' _name = 'account.move.line.update'
_description = 'Update non-legal fields of invoice lines' _description = 'Update non-legal fields of invoice lines'
_order = "sequence, name"
sequence = fields.Integer()
parent_id = fields.Many2one( parent_id = fields.Many2one(
'account.move.update', string='Wizard', ondelete='cascade') 'account.move.update', string='Wizard', ondelete='cascade')
invoice_line_id = fields.Many2one( invoice_line_id = fields.Many2one(
'account.move.line', string='Invoice Line', readonly=True) 'account.move.line', string='Invoice Line', readonly=True)
name = fields.Text(string='Description', required=True) name = fields.Text(string='Description', required=True)
display_type = fields.Selection([ display_type = fields.Selection(
('line_section', "Section"), related="invoice_line_id.display_type",
('line_note', "Note")], default=False, help="Technical field for UX purpose.") help="Technical field for UX purpose.")
quantity = fields.Float( quantity = fields.Float(
string='Quantity', digits='Product Unit of Measure', readonly=True) string='Quantity', digits='Product Unit of Measure', readonly=True)
price_subtotal = fields.Monetary( price_subtotal = fields.Monetary(
string='Amount', readonly=True) string='Amount', readonly=True)
analytic_account_id = fields.Many2one(
'account.analytic.account', string='Analytic Account')
analytic_tag_ids = fields.Many2many(
'account.analytic.tag', string='Analytic Tags')
currency_id = fields.Many2one('res.currency', readonly=True) currency_id = fields.Many2one('res.currency', readonly=True)
analytic_distribution = fields.Json(
string="Analytic",
# compute="_compute_writeoff_analytic_distribution",
# readonly=False,
# store=True,
# precompute=True,
)
analytic_precision = fields.Integer(
default=lambda self: self.env["decimal.precision"].precision_get(
"Percentage Analytic"
),
)

View File

@@ -15,25 +15,33 @@
<field name="move_type" invisible="1"/> <field name="move_type" invisible="1"/>
<field name="company_id" invisible="1"/> <field name="company_id" invisible="1"/>
<field name="partner_id" invisible="1"/> <field name="partner_id" invisible="1"/>
<field string="Bill Reference" attrs="{'invisible': [('move_type', 'not in', ('in_invoice', 'in_refund'))]}" name="ref"/> <field string="Bill Date" invisible="move_type not in ('in_invoice', 'in_refund')" name="invoice_date"/>
<field string="Customer Reference" attrs="{'invisible': [('move_type', 'not in', ('out_invoice', 'out_refund'))]}" name="ref"/> <field string="Bill Reference" invisible="move_type not in ('in_invoice', 'in_refund')" name="ref"/>
<field name="invoice_origin"/> <field string="Customer Reference" invisible="move_type not in ('out_invoice', 'out_refund')" name="ref"/>
<field name="invoice_payment_term_id" widget="selection"/> <field string="Ref" invisible="move_type != 'entry'" name="ref"/>
<field name="partner_bank_id"/> <field name="invoice_origin" invisible="move_type == 'entry'"/>
<field name="user_id" options="{'no_open': True, 'no_create': True, 'no_create_edit': True}"/> <!-- update of payment term is broken -->
<!-- <field name="invoice_payment_term_id" widget="selection"/>-->
<field name="partner_bank_id" invisible="move_type == 'entry'"/>
<field name="invoice_user_id" options="{'no_open': True, 'no_create': True, 'no_create_edit': True}" invisible="move_type == 'entry'"/>
</group> </group>
<group name="lines"> <group name="lines">
<field name="line_ids" nolabel="1"> <field name="line_ids" nolabel="1" widget="section_and_note_one2many">
<tree editable="bottom" create="false" delete="false" edit="true"> <list editable="bottom" create="false" delete="false" edit="true">
<field name="invoice_line_id" invisible="1"/> <field name="invoice_line_id" column_invisible="1"/>
<field name="display_type" invisible="1"/> <field name="display_type" column_invisible="1"/>
<field name="currency_id" invisible="1"/> <field name="currency_id" column_invisible="1"/>
<field name="name"/> <field name="name"/>
<field name="quantity" attrs="{'invisible': [('display_type', '!=', False)]}"/> <field name="quantity" invisible="display_type != 'product'" column_invisible="parent.move_type == 'entry'"/>
<field name="price_subtotal" attrs="{'invisible': [('display_type', '!=', False)]}"/> <field name="price_subtotal" invisible="display_type != 'product'" column_invisible="parent.move_type == 'entry'"/>
<field name="analytic_account_id" attrs="{'invisible': [('display_type', '!=', False)]}" groups="analytic.group_analytic_accounting"/> <field
<field name="analytic_tag_ids" attrs="{'invisible': [('display_type', '!=', False)]}" groups="analytic.group_analytic_tags" widget="many2many_tags"/> name="analytic_distribution"
</tree> widget="analytic_distribution"
groups="analytic.group_analytic_accounting"
options="{'account_field': 'account_id', 'business_domain': 'general'}"
invisible="display_type != 'product'"
/>
</list>
</field> </field>
</group> </group>
<footer> <footer>

View File

@@ -7,7 +7,7 @@ Account Usability
!! This file is generated by oca-gen-addon-readme !! !! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !! !! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:eb36005cad5a80be7fad1bfb83a2214419a8cae472545cc920545e6168d10231 !! source digest: sha256:dbf367e7cf8330c803622d0c171cd10e128344d318466d6e37ad7b0fc812e152
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png

View File

@@ -22,6 +22,7 @@
'views/account_journal.xml', 'views/account_journal.xml',
'views/account_move.xml', 'views/account_move.xml',
'views/account_move_line.xml', 'views/account_move_line.xml',
'views/account_payment.xml',
'views/account_analytic_line.xml', 'views/account_analytic_line.xml',
'views/account_menu.xml', 'views/account_menu.xml',
'views/account_tax.xml', 'views/account_tax.xml',

View File

@@ -367,7 +367,7 @@ ul.auto-toc {
!! This file is generated by oca-gen-addon-readme !! !! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !! !! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:eb36005cad5a80be7fad1bfb83a2214419a8cae472545cc920545e6168d10231 !! source digest: sha256:dbf367e7cf8330c803622d0c171cd10e128344d318466d6e37ad7b0fc812e152
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! --> !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/akretion/odoo-usability/tree/18.0/account_usability_akretion"><img alt="akretion/odoo-usability" src="https://img.shields.io/badge/github-akretion%2Fodoo--usability-lightgray.png?logo=github" /></a></p> <p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/akretion/odoo-usability/tree/18.0/account_usability_akretion"><img alt="akretion/odoo-usability" src="https://img.shields.io/badge/github-akretion%2Fodoo--usability-lightgray.png?logo=github" /></a></p>
<p>This modules adds the following functions:</p> <p>This modules adds the following functions:</p>

View File

@@ -16,7 +16,7 @@
<attribute name="class">btn-default</attribute> <attribute name="class">btn-default</attribute>
</button> </button>
<button name="action_register_payment" position="before"> <button name="action_register_payment" position="before">
<button name="%(account.account_invoices)d" type="action" string="Print" invisible="move_type not in ('out_invoice', 'out_refund')"/> <button name="action_print_pdf" type="object" string="Print" invisible="move_type not in ('out_invoice', 'out_refund') or state != 'draft'"/>
</button> </button>
<button name="preview_invoice" position="attributes"> <button name="preview_invoice" position="attributes">
<attribute name="invisible">1</attribute> <attribute name="invisible">1</attribute>

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2025 Akretion France (https://www.akretion.com/)
@author: Alexis de Lattre <alexis.delattre@akretion.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->
<odoo>
<record id="view_account_payment_form" model="ir.ui.view">
<field name="model">account.payment</field>
<field name="inherit_id" ref="account.view_account_payment_form"/>
<field name="arch" type="xml">
<group name="group2" position="inside">
<field name="move_id" readonly="1"/>
</group>
</field>
</record>
</odoo>

View File

@@ -16,4 +16,15 @@
</xpath> </xpath>
</field> </field>
</record> </record>
<record id="res_partner_view_tree" model="ir.ui.view">
<field name="model">res.partner</field>
<field name="inherit_id" ref="account.res_partner_view_tree"/>
<field name="arch" type="xml">
<field name="invoice_edi_format" position="after">
<field name="property_account_position_id" optional="hide"/>
</field>
</field>
</record>
</odoo> </odoo>

View File

@@ -2,10 +2,13 @@
Base Company Extension Base Company Extension
====================== ======================
.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !! !! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !! !! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:075f72950033a5c6f57ecfa5c2d101bd874dd2ae29adcecdc4905b378e89a3a2
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
:target: https://odoo-community.org/page/development-status :target: https://odoo-community.org/page/development-status
@@ -14,25 +17,29 @@ Base Company Extension
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3 :alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-akretion%2Fodoo--usability-lightgray.png?logo=github .. |badge3| image:: https://img.shields.io/badge/github-akretion%2Fodoo--usability-lightgray.png?logo=github
:target: https://github.com/akretion/odoo-usability/tree/12.0/base_company_extension :target: https://github.com/akretion/odoo-usability/tree/18.0/base_company_extension
:alt: akretion/odoo-usability :alt: akretion/odoo-usability
|badge1| |badge2| |badge3| |badge1| |badge2| |badge3|
This module adds the following fields on the company:
This module adds the following fields to the ResCompany model:
* Capital Amount * Capital Amount
* Legal Type * Legal Type
This is useful to display the legal name of the company in reports. This is useful to display the legal name of the company in reports
**Table of contents**
.. contents::
:local:
Bug Tracker Bug Tracker
=========== ===========
Bugs are tracked on `GitHub Issues <https://github.com/akretion/odoo-usability/issues>`_. Bugs are tracked on `GitHub Issues <https://github.com/akretion/odoo-usability/issues>`_.
In case of trouble, please check there if your issue has already been reported. In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us smashing it by providing a detailed and welcomed If you spotted it first, help us to smash it by providing a detailed and welcomed
`feedback <https://github.com/akretion/odoo-usability/issues/new?body=module:%20base_company_extension%0Aversion:%2014.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_. `feedback <https://github.com/akretion/odoo-usability/issues/new?body=module:%20base_company_extension%0Aversion:%2018.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
Do not contact contributors directly about support or help with technical issues. Do not contact contributors directly about support or help with technical issues.
@@ -52,6 +59,6 @@ Contributors
Maintainers Maintainers
~~~~~~~~~~~ ~~~~~~~~~~~
This module is part of the `akretion/odoo-usability <https://github.com/akretion/odoo-usability/tree/12.0/base_company_extension>`_ project on GitHub. This module is part of the `akretion/odoo-usability <https://github.com/akretion/odoo-usability/tree/18.0/base_company_extension>`_ project on GitHub.
You are welcome to contribute. You are welcome to contribute.

View File

@@ -4,14 +4,14 @@
{ {
'name': 'Base Company Extension', 'name': 'Base Company Extension',
'version': '16.0.1.0.0', 'version': '18.0.1.0.0',
'category': 'Partner', 'category': 'Partner',
'license': 'AGPL-3', 'license': 'AGPL-3',
'summary': 'Adds capital and title on company', 'summary': 'Adds capital and title on company',
'author': 'Akretion', 'author': 'Akretion',
'website': 'https://github.com/akretion/odoo-usability', 'website': 'https://github.com/akretion/odoo-usability',
# I depend on base_usability only for _report_company_legal_name() # I depend on base_usability only for _report_company_legal_name()
'depends': ['base_usability'], 'depends': ['base_usability_akretion'],
'data': ['views/res_company.xml'], 'data': ['views/res_company.xml'],
'installable': False, 'installable': True,
} }

View File

@@ -0,0 +1,3 @@
[build-system]
requires = ["whool"]
build-backend = "whool.buildapi"

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

View File

@@ -1,20 +1,20 @@
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head> <head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils 0.15.1: http://docutils.sourceforge.net/" /> <meta name="generator" content="Docutils: https://docutils.sourceforge.io/" />
<title>Base Company Extension</title> <title>Base Company Extension</title>
<style type="text/css"> <style type="text/css">
/* /*
:Author: David Goodger (goodger@python.org) :Author: David Goodger (goodger@python.org)
:Id: $Id: html4css1.css 7952 2016-07-26 18:15:59Z milde $ :Id: $Id: html4css1.css 9511 2024-01-13 09:50:07Z milde $
:Copyright: This stylesheet has been placed in the public domain. :Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils. Default cascading style sheet for the HTML output of Docutils.
Despite the name, some widely supported CSS2 features are used.
See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to See https://docutils.sourceforge.io/docs/howto/html-stylesheets.html for how to
customize this style sheet. customize this style sheet.
*/ */
@@ -275,7 +275,7 @@ pre.literal-block, pre.doctest-block, pre.math, pre.code {
margin-left: 2em ; margin-left: 2em ;
margin-right: 2em } margin-right: 2em }
pre.code .ln { color: grey; } /* line numbers */ pre.code .ln { color: gray; } /* line numbers */
pre.code, code { background-color: #eeeeee } pre.code, code { background-color: #eeeeee }
pre.code .comment, code .comment { color: #5C6576 } pre.code .comment, code .comment { color: #5C6576 }
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold } pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
@@ -301,7 +301,7 @@ span.option {
span.pre { span.pre {
white-space: pre } white-space: pre }
span.problematic { span.problematic, pre.problematic {
color: red } color: red }
span.section-subtitle { span.section-subtitle {
@@ -366,8 +366,10 @@ ul.auto-toc {
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! <!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !! !! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !! !! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:075f72950033a5c6f57ecfa5c2d101bd874dd2ae29adcecdc4905b378e89a3a2
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! --> !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external" href="https://github.com/akretion/odoo-usability/tree/12.0/base_company_extension"><img alt="akretion/odoo-usability" src="https://img.shields.io/badge/github-akretion%2Fodoo--usability-lightgray.png?logo=github" /></a></p> <p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/akretion/odoo-usability/tree/18.0/base_company_extension"><img alt="akretion/odoo-usability" src="https://img.shields.io/badge/github-akretion%2Fodoo--usability-lightgray.png?logo=github" /></a></p>
<p>This module adds the following fields to the ResCompany model: <p>This module adds the following fields to the ResCompany model:
* Capital Amount * Capital Amount
* Legal Type</p> * Legal Type</p>
@@ -375,40 +377,40 @@ ul.auto-toc {
<p><strong>Table of contents</strong></p> <p><strong>Table of contents</strong></p>
<div class="contents local topic" id="contents"> <div class="contents local topic" id="contents">
<ul class="simple"> <ul class="simple">
<li><a class="reference internal" href="#bug-tracker" id="id1">Bug Tracker</a></li> <li><a class="reference internal" href="#bug-tracker" id="toc-entry-1">Bug Tracker</a></li>
<li><a class="reference internal" href="#credits" id="id2">Credits</a><ul> <li><a class="reference internal" href="#credits" id="toc-entry-2">Credits</a><ul>
<li><a class="reference internal" href="#authors" id="id3">Authors</a></li> <li><a class="reference internal" href="#authors" id="toc-entry-3">Authors</a></li>
<li><a class="reference internal" href="#contributors" id="id4">Contributors</a></li> <li><a class="reference internal" href="#contributors" id="toc-entry-4">Contributors</a></li>
<li><a class="reference internal" href="#maintainers" id="id5">Maintainers</a></li> <li><a class="reference internal" href="#maintainers" id="toc-entry-5">Maintainers</a></li>
</ul> </ul>
</li> </li>
</ul> </ul>
</div> </div>
<div class="section" id="bug-tracker"> <div class="section" id="bug-tracker">
<h1><a class="toc-backref" href="#id1">Bug Tracker</a></h1> <h1><a class="toc-backref" href="#toc-entry-1">Bug Tracker</a></h1>
<p>Bugs are tracked on <a class="reference external" href="https://github.com/akretion/odoo-usability/issues">GitHub Issues</a>. <p>Bugs are tracked on <a class="reference external" href="https://github.com/akretion/odoo-usability/issues">GitHub Issues</a>.
In case of trouble, please check there if your issue has already been reported. In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us smashing it by providing a detailed and welcomed If you spotted it first, help us to smash it by providing a detailed and welcomed
<a class="reference external" href="https://github.com/akretion/odoo-usability/issues/new?body=module:%20base_company_extension%0Aversion:%2012.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p> <a class="reference external" href="https://github.com/akretion/odoo-usability/issues/new?body=module:%20base_company_extension%0Aversion:%2018.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
<p>Do not contact contributors directly about support or help with technical issues.</p> <p>Do not contact contributors directly about support or help with technical issues.</p>
</div> </div>
<div class="section" id="credits"> <div class="section" id="credits">
<h1><a class="toc-backref" href="#id2">Credits</a></h1> <h1><a class="toc-backref" href="#toc-entry-2">Credits</a></h1>
<div class="section" id="authors"> <div class="section" id="authors">
<h2><a class="toc-backref" href="#id3">Authors</a></h2> <h2><a class="toc-backref" href="#toc-entry-3">Authors</a></h2>
<ul class="simple"> <ul class="simple">
<li>Akretion</li> <li>Akretion</li>
</ul> </ul>
</div> </div>
<div class="section" id="contributors"> <div class="section" id="contributors">
<h2><a class="toc-backref" href="#id4">Contributors</a></h2> <h2><a class="toc-backref" href="#toc-entry-4">Contributors</a></h2>
<ul class="simple"> <ul class="simple">
<li>Alexis de Lattre &lt;<a class="reference external" href="mailto:alexis.delattre&#64;akretion.com">alexis.delattre&#64;akretion.com</a>&gt;</li> <li>Alexis de Lattre &lt;<a class="reference external" href="mailto:alexis.delattre&#64;akretion.com">alexis.delattre&#64;akretion.com</a>&gt;</li>
</ul> </ul>
</div> </div>
<div class="section" id="maintainers"> <div class="section" id="maintainers">
<h2><a class="toc-backref" href="#id5">Maintainers</a></h2> <h2><a class="toc-backref" href="#toc-entry-5">Maintainers</a></h2>
<p>This module is part of the <a class="reference external" href="https://github.com/akretion/odoo-usability/tree/12.0/base_company_extension">akretion/odoo-usability</a> project on GitHub.</p> <p>This module is part of the <a class="reference external" href="https://github.com/akretion/odoo-usability/tree/18.0/base_company_extension">akretion/odoo-usability</a> project on GitHub.</p>
<p>You are welcome to contribute.</p> <p>You are welcome to contribute.</p>
</div> </div>
</div> </div>

View File

@@ -4,7 +4,7 @@
{ {
'name': 'Mail Sender Bcc', 'name': 'Mail Sender Bcc',
'version': '16.0.1.0.0', 'version': '18.0.1.0.0',
'category': 'Mail', 'category': 'Mail',
'license': 'AGPL-3', 'license': 'AGPL-3',
'summary': "Always send a copy of the mail to the sender", 'summary': "Always send a copy of the mail to the sender",
@@ -17,5 +17,5 @@ With this module, when Odoo sends an outgoing email, it adds the sender as Bcc (
'author': 'Akretion', 'author': 'Akretion',
'website': 'https://github.com/akretion/odoo-usability', 'website': 'https://github.com/akretion/odoo-usability',
'depends': ['base'], 'depends': ['base'],
'installable': False, 'installable': True,
} }

View File

@@ -0,0 +1,3 @@
[build-system]
requires = ["whool"]
build-backend = "whool.buildapi"

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

View File

View File

@@ -0,0 +1,43 @@
# Copyright 2025 Akretion France (https://www.akretion.com)
# @author Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{
'name': 'Base Profile by Akretion',
'version': '18.0.1.0.0',
'category': 'Tools',
'license': 'AGPL-3',
'summary': 'Base module set selected by Alexis de Lattre',
'author': 'Akretion',
'website': 'https://github.com/akretion/odoo-usability',
'depends': [
# PARTNER
'partner_firstname', # OCA/partner-contact
'partner_email_duplicate_warn', # OCA/partner-contact
'partner_mobile_duplicate_warn', # OCA/partner-contact
'contacts', # official addons
# AUTH
'auth_admin_passkey', # OCA/server-auth
# REMOVE or FIX BAD NATIVE STUFF
'disable_odoo_online', # OCA/server-brand
'remove_odoo_enterprise', # OCA/server-brand
'mail_debrand', # OCA/mail
'partner_disable_gravatar', # OCA/partner-contact
'base_technical_features', # OCA/server-ux
### WEB
'web_responsive', # OCA/web
'web_environment_ribbon', # OCA/web
'web_no_bubble', # OCA/web
'web_dialog_size', # OCA/web
'web_chatter_position', # OCA/web
### MISC
'base_usability_akretion', # akretion/odoo-usability
'mail_usability_akretion', # akretion/odoo-usability
'eradicate_quick_create', # akretion/odoo-usability
'base_company_extension', # akretion/odoo-usability
# password_security will be enabled when the move to ir.config_parameter
# will be backported
#'password_security', # OCA/server-auth
],
'installable': True,
}

View File

@@ -0,0 +1,3 @@
[build-system]
requires = ["whool"]
build-backend = "whool.buildapi"

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

View File

@@ -1,24 +0,0 @@
diff --git a/addons/web/static/src/search/filter_menu/custom_filter_item.js b/addons/web/static/src/search/filter_menu/custom_filter_item.js
index f67f5fb40af..22525b7cbfd 100644
--- a/addons/web/static/src/search/filter_menu/custom_filter_item.js
+++ b/addons/web/static/src/search/filter_menu/custom_filter_item.js
@@ -46,6 +46,8 @@ const FIELD_OPERATORS = {
char: [
{ symbol: "ilike", description: _lt("contains") },
{ symbol: "not ilike", description: _lt("doesn't contain") },
+ { symbol: "startswith", description: _lt("starts with") },
+ { symbol: "endswith", description: _lt("ends with") },
{ symbol: "=", description: _lt("is equal to") },
{ symbol: "!=", description: _lt("is not equal to") },
{ symbol: "!=", description: _lt("is set"), value: false },
@@ -257,6 +259,10 @@ export class CustomFilterItem extends Component {
[field.name, ">=", domainValue[0]],
[field.name, "<=", domainValue[1]]
);
+ } else if (operator.symbol === "startswith") {
+ domainArray.push([field.name, '=ilike', domainValue[0] + '%']);
+ } else if (operator.symbol === "endswith") {
+ domainArray.push([field.name, '=ilike', '%' + domainValue[0]]);
} else {
domainArray.push([field.name, operator.symbol, domainValue[0]]);
}

View File

@@ -1,2 +1,3 @@
from . import models from . import models
from . import wizards from . import wizards
from . import reports

View File

@@ -4,7 +4,7 @@
{ {
'name': 'Commission Simple', 'name': 'Commission Simple',
'version': '16.0.1.0.0', 'version': '18.0.1.0.0',
'category': 'Sales', 'category': 'Sales',
'license': 'AGPL-3', 'license': 'AGPL-3',
'summary': 'Compute commissions for salesman', 'summary': 'Compute commissions for salesman',
@@ -30,19 +30,18 @@ This module has been written by Alexis de Lattre from Akretion
'depends': [ 'depends': [
'account', 'account',
'date_range', 'date_range',
# this uses some related fields on account.move.line 'report_xlsx',
# 'account_usability_akretion',
], ],
'data': [ 'data': [
'security/ir.model.access.csv', 'security/ir.model.access.csv',
'security/rule.xml', 'security/rule.xml',
'reports/report.xml',
'data/decimal_precision.xml', 'data/decimal_precision.xml',
'views/commission_profile.xml', 'views/commission_profile.xml',
'views/commission_rule.xml', 'views/commission_rule.xml',
'views/commission_result.xml', 'views/commission_result.xml',
'views/account_move_line.xml', 'views/account_move_line.xml',
'views/res_config_settings.xml',
'wizards/commission_compute_view.xml', 'wizards/commission_compute_view.xml',
], ],
'installable': False, 'installable': True,
} }

View File

@@ -6,15 +6,25 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: Odoo Server 16.0\n" "Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-11-29 23:38+0000\n" "POT-Creation-Date: 2025-09-02 11:22+0000\n"
"PO-Revision-Date: 2024-11-29 23:38+0000\n" "PO-Revision-Date: 2025-09-02 11:23+0000\n"
"Last-Translator: \n" "Last-Translator: Alexis de Lattre <alexis.delattre@akretion.com>\n"
"Language-Team: \n" "Language-Team: \n"
"Language: fr\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n" "Content-Transfer-Encoding: \n"
"Plural-Forms: \n" "Plural-Forms: \n"
#. module: commission_simple
#: model:ir.actions.report,print_report_name:commission_simple.commission_result_xlsx_report
msgid ""
"'commission-%s-%s' % (object.date_range_id.name.replace(' ', '_'), object."
"partner_id.name.replace(' ', '_'))"
msgstr ""
"'commission-%s-%s' % (object.date_range_id.name.replace(' ', '_'), object."
"partner_id.name.replace(' ', '_'))"
#. module: commission_simple #. module: commission_simple
#: model:ir.model.constraint,message:commission_simple.constraint_commission_result_salesman_period_company_unique #: model:ir.model.constraint,message:commission_simple.constraint_commission_result_salesman_period_company_unique
msgid "" msgid ""
@@ -33,6 +43,13 @@ msgstr ""
"Un vendeur doit être sélectionné lorsque le type d'affectation est " "Un vendeur doit être sélectionné lorsque le type d'affectation est "
"\"Vendeur\"." "\"Vendeur\"."
#. module: commission_simple
#. odoo-python
#: code:addons/commission_simple/models/commission_rule.py:0
#, python-format
msgid "AND"
msgstr "ET"
#. module: commission_simple #. module: commission_simple
#: model:ir.model.fields,field_description:commission_simple.field_commission_result__message_needaction #: model:ir.model.fields,field_description:commission_simple.field_commission_result__message_needaction
msgid "Action Needed" msgid "Action Needed"
@@ -108,13 +125,24 @@ msgid "Cancel"
msgstr "Annuler" msgstr "Annuler"
#. module: commission_simple #. module: commission_simple
#: model_terms:ir.ui.view,arch_db:commission_simple.view_move_line_form
msgid "Commission"
msgstr "Commission"
#. module: commission_simple
#. odoo-python
#: code:addons/commission_simple/reports/commission_result_xlsx.py:0
#: model:ir.model.fields,field_description:commission_simple.field_account_move_line__commission_amount #: model:ir.model.fields,field_description:commission_simple.field_account_move_line__commission_amount
#, python-format
msgid "Commission Amount" msgid "Commission Amount"
msgstr "Montant de la commission" msgstr "Montant de la commission"
#. module: commission_simple #. module: commission_simple
#. odoo-python
#: code:addons/commission_simple/reports/commission_result_xlsx.py:0
#: model:ir.model.fields,field_description:commission_simple.field_account_move_line__commission_base #: model:ir.model.fields,field_description:commission_simple.field_account_move_line__commission_base
#: model:ir.model.fields,field_description:commission_simple.field_commission_rule__base #: model:ir.model.fields,field_description:commission_simple.field_commission_rule__base
#, python-format
msgid "Commission Base" msgid "Commission Base"
msgstr "Base de la commission" msgstr "Base de la commission"
@@ -122,14 +150,14 @@ msgstr "Base de la commission"
#: model:ir.model.fields,field_description:commission_simple.field_commission_result__line_ids #: model:ir.model.fields,field_description:commission_simple.field_commission_result__line_ids
#: model_terms:ir.ui.view,arch_db:commission_simple.commission_result_form #: model_terms:ir.ui.view,arch_db:commission_simple.commission_result_form
msgid "Commission Lines" msgid "Commission Lines"
msgstr "Lignes commission" msgstr "Lignes de commission"
#. module: commission_simple #. module: commission_simple
#: model:ir.model.fields,field_description:commission_simple.field_commission_compute__date_range_type_id #: model:ir.model.fields,field_description:commission_simple.field_commission_compute__date_range_type_id
#: model:ir.model.fields,field_description:commission_simple.field_res_company__commission_date_range_type_id #: model:ir.model.fields,field_description:commission_simple.field_res_company__commission_date_range_type_id
#: model:ir.model.fields,field_description:commission_simple.field_res_config_settings__commission_date_range_type_id #: model:ir.model.fields,field_description:commission_simple.field_res_config_settings__commission_date_range_type_id
msgid "Commission Periodicity" msgid "Commission Periodicity"
msgstr "Périodicité de commission" msgstr "Périodicité des commissions"
#. module: commission_simple #. module: commission_simple
#: model:ir.model,name:commission_simple.model_commission_profile #: model:ir.model,name:commission_simple.model_commission_profile
@@ -140,7 +168,7 @@ msgstr "Profil de commission"
#. module: commission_simple #. module: commission_simple
#: model:ir.model,name:commission_simple.model_commission_profile_assignment #: model:ir.model,name:commission_simple.model_commission_profile_assignment
msgid "Commission Profile Assignment" msgid "Commission Profile Assignment"
msgstr "Affectation du profil de commission" msgstr "Affectation des profils de commission"
#. module: commission_simple #. module: commission_simple
#: model:ir.actions.act_window,name:commission_simple.commission_profile_action #: model:ir.actions.act_window,name:commission_simple.commission_profile_action
@@ -149,8 +177,11 @@ msgid "Commission Profiles"
msgstr "Profils de commission" msgstr "Profils de commission"
#. module: commission_simple #. module: commission_simple
#. odoo-python
#: code:addons/commission_simple/reports/commission_result_xlsx.py:0
#: model:ir.model.fields,field_description:commission_simple.field_account_move_line__commission_rate #: model:ir.model.fields,field_description:commission_simple.field_account_move_line__commission_rate
#: model:ir.model.fields,field_description:commission_simple.field_commission_rule__rate #: model:ir.model.fields,field_description:commission_simple.field_commission_rule__rate
#, python-format
msgid "Commission Rate" msgid "Commission Rate"
msgstr "Taux de commission" msgstr "Taux de commission"
@@ -160,6 +191,11 @@ msgstr "Taux de commission"
msgid "Commission Result" msgid "Commission Result"
msgstr "État des commissions" msgstr "État des commissions"
#. module: commission_simple
#: model:ir.model,name:commission_simple.model_report_commission_simple_report_xlsx
msgid "Commission Result XLSX"
msgstr "État des commission XLSX"
#. module: commission_simple #. module: commission_simple
#: model:ir.model,name:commission_simple.model_commission_rule #: model:ir.model,name:commission_simple.model_commission_rule
msgid "Commission Rule" msgid "Commission Rule"
@@ -184,7 +220,7 @@ msgstr "Total des commissions"
#: model:ir.ui.menu,name:commission_simple.commission_root #: model:ir.ui.menu,name:commission_simple.commission_root
#: model_terms:ir.ui.view,arch_db:commission_simple.res_config_settings_view_form #: model_terms:ir.ui.view,arch_db:commission_simple.res_config_settings_view_form
msgid "Commissions" msgid "Commissions"
msgstr "" msgstr "Commissions"
#. module: commission_simple #. module: commission_simple
#. odoo-python #. odoo-python
@@ -194,6 +230,13 @@ msgid "Commissions already exist for %(period)s in company %(company)s."
msgstr "" msgstr ""
"Des commissions existent déjà pour %(period)s dans la société %(company)s." "Des commissions existent déjà pour %(period)s dans la société %(company)s."
#. module: commission_simple
#. odoo-python
#: code:addons/commission_simple/reports/commission_result_xlsx.py:0
#, python-format
msgid "Commissions of %(partner)s for period %(period)s"
msgstr "Commissions de %(partner)s pour la période %(period)s"
#. module: commission_simple #. module: commission_simple
#: model:ir.model,name:commission_simple.model_res_company #: model:ir.model,name:commission_simple.model_res_company
msgid "Companies" msgid "Companies"
@@ -254,11 +297,37 @@ msgstr "Créé par"
msgid "Created on" msgid "Created on"
msgstr "Créé le" msgstr "Créé le"
#. module: commission_simple
#. odoo-python
#: code:addons/commission_simple/reports/commission_result_xlsx.py:0
#, python-format
msgid "Currency"
msgstr "Devise"
#. module: commission_simple
#. odoo-python
#: code:addons/commission_simple/reports/commission_result_xlsx.py:0
#, python-format
msgid "Customer"
msgstr "Client"
#. module: commission_simple #. module: commission_simple
#: model:ir.model.fields,field_description:commission_simple.field_commission_rule__partner_ids #: model:ir.model.fields,field_description:commission_simple.field_commission_rule__partner_ids
msgid "Customers" msgid "Customers"
msgstr "Clients" msgstr "Clients"
#. module: commission_simple
#. odoo-python
#: code:addons/commission_simple/models/commission_rule.py:0
#, python-format
msgid "Customers:"
msgstr "Clients :"
#. module: commission_simple
#: model_terms:ir.ui.view,arch_db:commission_simple.commission_result_form
msgid "Disc.%"
msgstr "Rem.%"
#. module: commission_simple #. module: commission_simple
#: model:ir.model.fields,field_description:commission_simple.field_commission_compute__display_name #: model:ir.model.fields,field_description:commission_simple.field_commission_compute__display_name
#: model:ir.model.fields,field_description:commission_simple.field_commission_profile__display_name #: model:ir.model.fields,field_description:commission_simple.field_commission_profile__display_name
@@ -272,7 +341,7 @@ msgstr "Nom affiché"
#: model:ir.model.fields.selection,name:commission_simple.selection__commission_result__state__done #: model:ir.model.fields.selection,name:commission_simple.selection__commission_result__state__done
#: model_terms:ir.ui.view,arch_db:commission_simple.commission_result_search #: model_terms:ir.ui.view,arch_db:commission_simple.commission_result_search
msgid "Done" msgid "Done"
msgstr "Validé" msgstr "Terminé"
#. module: commission_simple #. module: commission_simple
#: model:ir.model.fields.selection,name:commission_simple.selection__commission_result__state__draft #: model:ir.model.fields.selection,name:commission_simple.selection__commission_result__state__draft
@@ -281,7 +350,15 @@ msgid "Draft"
msgstr "Brouillon" msgstr "Brouillon"
#. module: commission_simple #. module: commission_simple
#: model:ir.actions.report,name:commission_simple.commission_result_xlsx_report
msgid "Détails Excel"
msgstr "Détails Excel"
#. module: commission_simple
#. odoo-python
#: code:addons/commission_simple/reports/commission_result_xlsx.py:0
#: model:ir.model.fields,field_description:commission_simple.field_commission_rule__date_end #: model:ir.model.fields,field_description:commission_simple.field_commission_rule__date_end
#, python-format
msgid "End Date" msgid "End Date"
msgstr "Date de fin" msgstr "Date de fin"
@@ -291,6 +368,11 @@ msgstr "Date de fin"
msgid "End date" msgid "End date"
msgstr "Date de fin" msgstr "Date de fin"
#. module: commission_simple
#: model_terms:ir.ui.view,arch_db:commission_simple.commission_result_form
msgid "Excel Export"
msgstr "Export Excel"
#. module: commission_simple #. module: commission_simple
#: model:ir.model.fields,field_description:commission_simple.field_commission_result__message_follower_ids #: model:ir.model.fields,field_description:commission_simple.field_commission_result__message_follower_ids
msgid "Followers" msgid "Followers"
@@ -307,9 +389,19 @@ msgid "Font awesome icon e.g. fa-tasks"
msgstr "Îcone font-awesome, par exemple fa-task" msgstr "Îcone font-awesome, par exemple fa-task"
#. module: commission_simple #. module: commission_simple
#. odoo-python
#: code:addons/commission_simple/reports/commission_result_xlsx.py:0
#, python-format
msgid "Generated from Odoo on %s by %s"
msgstr "Généré à partir d'Odoo le %s par %s"
#. module: commission_simple
#. odoo-python
#: code:addons/commission_simple/models/commission_rule.py:0
#: model:ir.model.fields.selection,name:commission_simple.selection__commission_rule__applied_on__4_global #: model:ir.model.fields.selection,name:commission_simple.selection__commission_rule__applied_on__4_global
#, python-format
msgid "Global" msgid "Global"
msgstr "" msgstr "Global"
#. module: commission_simple #. module: commission_simple
#: model:ir.model.fields,field_description:commission_simple.field_commission_result__has_message #: model:ir.model.fields,field_description:commission_simple.field_commission_result__has_message
@@ -323,7 +415,7 @@ msgstr "A un message"
#: model:ir.model.fields,field_description:commission_simple.field_commission_result__id #: model:ir.model.fields,field_description:commission_simple.field_commission_result__id
#: model:ir.model.fields,field_description:commission_simple.field_commission_rule__id #: model:ir.model.fields,field_description:commission_simple.field_commission_rule__id
msgid "ID" msgid "ID"
msgstr "" msgstr "ID"
#. module: commission_simple #. module: commission_simple
#: model:ir.model.fields,field_description:commission_simple.field_commission_result__activity_exception_icon #: model:ir.model.fields,field_description:commission_simple.field_commission_result__activity_exception_icon
@@ -350,6 +442,20 @@ msgstr "Si activé, des messages ont une erreur d'envoi."
msgid "In Payment and Paid" msgid "In Payment and Paid"
msgstr "En paiement et payé" msgstr "En paiement et payé"
#. module: commission_simple
#. odoo-python
#: code:addons/commission_simple/reports/commission_result_xlsx.py:0
#, python-format
msgid "Invoice"
msgstr "Facture"
#. module: commission_simple
#. odoo-python
#: code:addons/commission_simple/reports/commission_result_xlsx.py:0
#, python-format
msgid "Invoice Date"
msgstr "Date de facture"
#. module: commission_simple #. module: commission_simple
#: model:ir.model.fields.selection,name:commission_simple.selection__commission_profile__trigger_type__invoice #: model:ir.model.fields.selection,name:commission_simple.selection__commission_profile__trigger_type__invoice
msgid "Invoiced" msgid "Invoiced"
@@ -411,7 +517,12 @@ msgstr "Marge"
#. module: commission_simple #. module: commission_simple
#: model_terms:ir.ui.view,arch_db:commission_simple.commission_rule_form #: model_terms:ir.ui.view,arch_db:commission_simple.commission_rule_form
msgid "Match" msgid "Match"
msgstr "" msgstr "Correspondance"
#. module: commission_simple
#: model:ir.model.fields,field_description:commission_simple.field_commission_rule__apply_description
msgid "Match Criteria"
msgstr "Critères de correspondance"
#. module: commission_simple #. module: commission_simple
#: model:ir.model.fields,field_description:commission_simple.field_account_move_line__commission_rule_id #: model:ir.model.fields,field_description:commission_simple.field_account_move_line__commission_rule_id
@@ -426,7 +537,7 @@ msgstr "Erreur d'envoi du message"
#. module: commission_simple #. module: commission_simple
#: model:ir.model.fields,field_description:commission_simple.field_commission_result__message_ids #: model:ir.model.fields,field_description:commission_simple.field_commission_result__message_ids
msgid "Messages" msgid "Messages"
msgstr "" msgstr "Messages"
#. module: commission_simple #. module: commission_simple
#: model:ir.model.fields,field_description:commission_simple.field_commission_result__my_activity_date_deadline #: model:ir.model.fields,field_description:commission_simple.field_commission_result__my_activity_date_deadline
@@ -497,6 +608,18 @@ msgstr "Payé"
msgid "Period" msgid "Period"
msgstr "Période" msgstr "Période"
#. module: commission_simple
#: model_terms:ir.ui.view,arch_db:commission_simple.commission_result_form
msgid "Price"
msgstr "Prix"
#. module: commission_simple
#. odoo-python
#: code:addons/commission_simple/reports/commission_result_xlsx.py:0
#, python-format
msgid "Product"
msgstr "Produit"
#. module: commission_simple #. module: commission_simple
#: model:ir.model.fields,field_description:commission_simple.field_commission_rule__product_categ_ids #: model:ir.model.fields,field_description:commission_simple.field_commission_rule__product_categ_ids
#: model:ir.model.fields.selection,name:commission_simple.selection__commission_rule__applied_on__3_product_category #: model:ir.model.fields.selection,name:commission_simple.selection__commission_rule__applied_on__3_product_category
@@ -508,6 +631,13 @@ msgstr "Catégories de produits"
msgid "Product Categories and Customers" msgid "Product Categories and Customers"
msgstr "Catégories de produits et clients" msgstr "Catégories de produits et clients"
#. module: commission_simple
#. odoo-python
#: code:addons/commission_simple/models/commission_rule.py:0
#, python-format
msgid "Product Categories:"
msgstr "Catégories de produits :"
#. module: commission_simple #. module: commission_simple
#: model:ir.model.fields,field_description:commission_simple.field_account_move_line__product_categ_id #: model:ir.model.fields,field_description:commission_simple.field_account_move_line__product_categ_id
msgid "Product Category" msgid "Product Category"
@@ -524,6 +654,13 @@ msgstr "Produits"
msgid "Products and Customers" msgid "Products and Customers"
msgstr "Produits et clients" msgstr "Produits et clients"
#. module: commission_simple
#. odoo-python
#: code:addons/commission_simple/models/commission_rule.py:0
#, python-format
msgid "Products:"
msgstr "Produits :"
#. module: commission_simple #. module: commission_simple
#: model:ir.model.fields,field_description:commission_simple.field_commission_profile_assignment__profile_id #: model:ir.model.fields,field_description:commission_simple.field_commission_profile_assignment__profile_id
#: model:ir.model.fields,field_description:commission_simple.field_commission_rule__profile_id #: model:ir.model.fields,field_description:commission_simple.field_commission_rule__profile_id
@@ -531,6 +668,13 @@ msgstr "Produits et clients"
msgid "Profile" msgid "Profile"
msgstr "Profil" msgstr "Profil"
#. module: commission_simple
#. odoo-python
#: code:addons/commission_simple/reports/commission_result_xlsx.py:0
#, python-format
msgid "Quantity"
msgstr "Quantité"
#. module: commission_simple #. module: commission_simple
#: model_terms:ir.ui.view,arch_db:commission_simple.commission_result_form #: model_terms:ir.ui.view,arch_db:commission_simple.commission_result_form
#: model_terms:ir.ui.view,arch_db:commission_simple.commission_rule_tree #: model_terms:ir.ui.view,arch_db:commission_simple.commission_rule_tree
@@ -572,7 +716,10 @@ msgid "Sequence"
msgstr "Séquence" msgstr "Séquence"
#. module: commission_simple #. module: commission_simple
#. odoo-python
#: code:addons/commission_simple/reports/commission_result_xlsx.py:0
#: model:ir.model.fields,field_description:commission_simple.field_commission_rule__date_start #: model:ir.model.fields,field_description:commission_simple.field_commission_rule__date_start
#, python-format
msgid "Start Date" msgid "Start Date"
msgstr "Date de début" msgstr "Date de début"
@@ -605,6 +752,13 @@ msgstr ""
msgid "This salesman already has an assignment in this company." msgid "This salesman already has an assignment in this company."
msgstr "Ce vendeur a déjà une assignation dans cette société." msgstr "Ce vendeur a déjà une assignation dans cette société."
#. module: commission_simple
#. odoo-python
#: code:addons/commission_simple/reports/commission_result_xlsx.py:0
#, python-format
msgid "Total Amount"
msgstr "Montant total"
#. module: commission_simple #. module: commission_simple
#: model:ir.model.fields,field_description:commission_simple.field_commission_profile__trigger_type #: model:ir.model.fields,field_description:commission_simple.field_commission_profile__trigger_type
msgid "Trigger" msgid "Trigger"
@@ -613,13 +767,20 @@ msgstr "Déclencheur"
#. module: commission_simple #. module: commission_simple
#: model:ir.model.fields,field_description:commission_simple.field_commission_profile_assignment__assign_type #: model:ir.model.fields,field_description:commission_simple.field_commission_profile_assignment__assign_type
msgid "Type" msgid "Type"
msgstr "" msgstr "Type"
#. module: commission_simple #. module: commission_simple
#: model:ir.model.fields,help:commission_simple.field_commission_result__activity_exception_decoration #: model:ir.model.fields,help:commission_simple.field_commission_result__activity_exception_decoration
msgid "Type of the exception activity on record." msgid "Type of the exception activity on record."
msgstr "Type de l'activité-alerte sur l'enregistrement." msgstr "Type de l'activité-alerte sur l'enregistrement."
#. module: commission_simple
#. odoo-python
#: code:addons/commission_simple/reports/commission_result_xlsx.py:0
#, python-format
msgid "Unit"
msgstr "Unité"
#. module: commission_simple #. module: commission_simple
#: model:ir.model.fields,field_description:commission_simple.field_commission_result__website_message_ids #: model:ir.model.fields,field_description:commission_simple.field_commission_result__website_message_ids
msgid "Website Messages" msgid "Website Messages"
@@ -636,5 +797,5 @@ msgstr "Historique des échanges sur le site Web"
#, python-format #, python-format
msgid "You cannot delete commission result %s because it is in done state." msgid "You cannot delete commission result %s because it is in done state."
msgstr "" msgstr ""
"Vous ne pouvez pas supprimer l'état de commission %s parce qu'il est à " "Vous ne pouvez pas supprimer l'état de commission %s parce qu'il est "
"l'état \"validé\"." "à l'état \"terminé\"."

View File

@@ -1,5 +1,5 @@
from . import commission_profile from . import commission_profile
from . import commission_rule from . import commission_rule
from . import commission_result from . import commission_result
from . import res_company
from . import account_move_line from . import account_move_line
from . import account_invoice_report

View File

@@ -0,0 +1,16 @@
# Copyright 2018-2019 Akretion France (http://www.akretion.com)
# @author Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import api, fields, models
from odoo.tools import SQL
class AccountInvoiceReport(models.Model):
_inherit = 'account.invoice.report'
commission_amount = fields.Float(readonly=True)
@api.model
def _select(self) -> SQL:
return SQL("%s, line.commission_amount * account_currency_table.rate AS commission_amount", super()._select())

View File

@@ -11,7 +11,7 @@ class AccountMoveLine(models.Model):
_inherit = 'account.move.line' _inherit = 'account.move.line'
commission_result_id = fields.Many2one( commission_result_id = fields.Many2one(
'commission.result', string='Commission Result', check_company=True) 'commission.result', string='Commission Result', check_company=True, index=True)
commission_rule_id = fields.Many2one( commission_rule_id = fields.Many2one(
'commission.rule', 'Matched Commission Rule', ondelete='restrict', check_company=True) 'commission.rule', 'Matched Commission Rule', ondelete='restrict', check_company=True)
commission_base = fields.Monetary('Commission Base', currency_field='company_currency_id') commission_base = fields.Monetary('Commission Base', currency_field='company_currency_id')
@@ -88,3 +88,18 @@ class AccountMoveLine(models.Model):
if float_is_zero(lvals['commission_rate'], precision_digits=rate_prec) or self.company_currency_id.is_zero(lvals['commission_base']): if float_is_zero(lvals['commission_rate'], precision_digits=rate_prec) or self.company_currency_id.is_zero(lvals['commission_base']):
return False return False
return lvals return lvals
def _prepare_commission_xlsx(self):
self.ensure_one()
vals = {
"inv.name": self.move_id.name,
"inv.date": self.move_id.invoice_date,
"inv.partner": self.move_id.commercial_partner_id.display_name,
"product": self.product_id and self.product_id.display_name or self.name,
"qty": self.quantity,
"uom": self.product_uom_id.name,
"commission_base": self.commission_base,
"commission_rate": self.commission_rate / 100,
"commission_amount": self.commission_amount,
}
return vals

View File

@@ -27,6 +27,9 @@ class CommissionProfile(models.Model):
('paid', 'Paid'), ('paid', 'Paid'),
('in_payment', 'In Payment and Paid'), ('in_payment', 'In Payment and Paid'),
], default='paid', string='Trigger', required=True) ], default='paid', string='Trigger', required=True)
date_range_type_id = fields.Many2one(
'date.range.type', string='Commission Periodicity', ondelete='restrict',
domain="[('company_id', 'in', (False, company_id))]")
class CommissionProfileAssignment(models.Model): class CommissionProfileAssignment(models.Model):
@@ -100,6 +103,7 @@ class CommissionProfileAssignment(models.Model):
'profile_id': self.profile_id.id, 'profile_id': self.profile_id.id,
'date_range_id': date_range.id, 'date_range_id': date_range.id,
'assign_type': self.assign_type, 'assign_type': self.assign_type,
'assignment_id': self.id,
'company_id': self.company_id.id, 'company_id': self.company_id.id,
} }
return vals return vals

View File

@@ -17,6 +17,8 @@ class CommissionResult(models.Model):
readonly=True, tracking=True) readonly=True, tracking=True)
profile_id = fields.Many2one( profile_id = fields.Many2one(
'commission.profile', string='Commission Profile', readonly=True, tracking=True) 'commission.profile', string='Commission Profile', readonly=True, tracking=True)
assignment_id = fields.Many2one(
'commission.profile.assignment', string="Commission Profile Assignment", readonly=True)
assign_type = fields.Selection('_assign_type_selection', readonly=True, tracking=True) assign_type = fields.Selection('_assign_type_selection', readonly=True, tracking=True)
company_id = fields.Many2one( company_id = fields.Many2one(
'res.company', string='Company', ondelete='cascade', 'res.company', string='Company', ondelete='cascade',
@@ -32,7 +34,10 @@ class CommissionResult(models.Model):
states={'done': [('readonly', True)]}) states={'done': [('readonly', True)]})
amount_total = fields.Monetary( amount_total = fields.Monetary(
string='Commission Total', currency_field='company_currency_id', string='Commission Total', currency_field='company_currency_id',
compute='_compute_amount_total', store=True, tracking=True) compute='_compute_totals', store=True, tracking=True)
base_total = fields.Monetary(
string="Commission Base Total", currency_field='company_currency_id',
compute='_compute_totals', store=True, tracking=True)
state = fields.Selection([ state = fields.Selection([
('draft', 'Draft'), ('draft', 'Draft'),
('done', 'Done'), ('done', 'Done'),
@@ -44,12 +49,13 @@ class CommissionResult(models.Model):
def _assign_type_selection(self): def _assign_type_selection(self):
return self.env['commission.profile.assignment']._assign_type_selection() return self.env['commission.profile.assignment']._assign_type_selection()
@api.depends('line_ids.commission_amount') @api.depends('line_ids.commission_amount', 'line_ids.commission_base')
def _compute_amount_total(self): def _compute_totals(self):
rg_res = self.env['account.move.line'].read_group([('commission_result_id', 'in', self.ids)], ['commission_result_id', 'commission_amount:sum'], ['commission_result_id']) rg_res = self.env['account.move.line'].read_group([('commission_result_id', 'in', self.ids)], ['commission_result_id', 'commission_amount:sum', 'commission_base:sum'], ['commission_result_id'])
mapped_data = dict([(x['commission_result_id'][0], x['commission_amount']) for x in rg_res]) mapped_data = dict([(x['commission_result_id'][0], {'amount': x['commission_amount'], 'base': x['commission_base']}) for x in rg_res])
for rec in self: for rec in self:
rec.amount_total = mapped_data.get(rec.id, 0) rec.amount_total = mapped_data.get(rec.id, {}).get('amount')
rec.base_total = mapped_data.get(rec.id, {}).get('base')
def unlink(self): def unlink(self):
for result in self: for result in self:
@@ -59,7 +65,7 @@ class CommissionResult(models.Model):
return super().unlink() return super().unlink()
def draft2done(self): def draft2done(self):
self.write({'state': 'done'}) self.filtered(lambda x: x.state == 'draft').write({'state': 'done'})
def backtodraft(self): def backtodraft(self):
self.write({'state': 'draft'}) self.write({'state': 'draft'})
@@ -75,3 +81,7 @@ class CommissionResult(models.Model):
'salesman_period_company_unique', 'salesman_period_company_unique',
'unique(company_id, partner_id, date_range_id)', 'unique(company_id, partner_id, date_range_id)',
'A commission result already exists for this salesman/agent for the same period.')] 'A commission result already exists for this salesman/agent for the same period.')]
def _prepare_xlsx_lines(self):
self.ensure_one()
return self.line_ids.sorted(key=lambda x: x.move_id.invoice_date)

View File

@@ -3,13 +3,13 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import fields, models, api from odoo import fields, models, api, _
class CommissionRule(models.Model): class CommissionRule(models.Model):
_name = 'commission.rule' _name = 'commission.rule'
_description = 'Commission Rule' _description = 'Commission Rule'
_order = 'profile_id, applied_on' _order = 'profile_id, applied_on, rate desc'
partner_ids = fields.Many2many( partner_ids = fields.Many2many(
'res.partner', string='Customers', domain=[('parent_id', '=', False)]) 'res.partner', string='Customers', domain=[('parent_id', '=', False)])
@@ -34,6 +34,31 @@ class CommissionRule(models.Model):
('4_global', 'Global')], ('4_global', 'Global')],
string='Apply On', default='4_global', required=True) string='Apply On', default='4_global', required=True)
active = fields.Boolean(string='Active', default=True) active = fields.Boolean(string='Active', default=True)
apply_description = fields.Html(compute='_compute_apply_description', string="Match Criteria")
_sql_constraints = [(
'rate_positive',
'CHECK(rate >= 0)',
'Rate must be positive !')]
def _compute_apply_description(self):
customer_label = "<strong>" + _("Customers:") + "</strong>"
product_label = "<strong>" + _("Products:") + "</strong>"
product_categ_label = "<strong>" + _("Product Categories:") + "</strong>"
and_label = "<strong>" + _('AND') + "</strong>"
for rule in self:
desc = False
if rule.applied_on == '0_customer_product':
desc = f"{customer_label} {', '.join([part.ref or part.name for part in rule.partner_ids])} {and_label} {product_label} {', '.join([pp.default_code or pp.name for pp in rule.product_ids])}"
elif rule.applied_on == '1_customer_product_category':
desc = f"{customer_label} {', '.join([part.ref or part.name for part in rule.partner_ids])} {and_label} {product_categ_label} {', '.join([categ.display_name for categ in rule.product_categ_ids])}"
elif rule.applied_on == '2_product':
desc = f"{product_label} {', '.join([pp.default_code or pp.name for pp in rule.product_ids])}"
elif rule.applied_on == '3_product_category':
desc = f"{product_categ_label} {', '.join([categ.display_name for categ in rule.product_categ_ids])}"
elif rule.applied_on == '4_global':
desc = _('Global')
rule.apply_description = desc
@api.model @api.model
def load_all_rules(self): def load_all_rules(self):
@@ -45,8 +70,3 @@ class CommissionRule(models.Model):
else: else:
res[rule['profile_id'][0]].append(rule) res[rule['profile_id'][0]].append(rule)
return res return res
_sql_constraints = [(
'rate_positive',
'CHECK(rate >= 0)',
'Rate must be positive !')]

View File

@@ -1,13 +0,0 @@
# Copyright 2019-2024 Akretion France (https://www.akretion.com/)
# @author Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import fields, models
class ResCompany(models.Model):
_inherit = 'res.company'
commission_date_range_type_id = fields.Many2one(
'date.range.type', string='Commission Periodicity', ondelete='restrict')

View File

@@ -1,20 +0,0 @@
# Copyright 2019-2024 Akretion France (https://www.akretion.com/)
# @author Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import fields, models
class ResUsers(models.Model):
_inherit = 'res.users'
# TODO mon idée : déplacer ça dans une table dédiée
# company_id oblig
# partner_id (filtré... sur lien vers user ou agent petit difficulté)
# profile_id
# type agent ou user => ça donne le champ de recherche
commission_profile_id = fields.Many2one(
'commission.profile', string='Commission Profile',
company_dependent=True)

View File

@@ -0,0 +1 @@
from . import commission_result_xlsx

View File

@@ -0,0 +1,112 @@
# Copyright 2025 Akretion France (https://www.akretion.com/)
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import api, fields, models, tools, Command, _
from odoo.exceptions import UserError
from datetime import datetime
from odoo.tools.misc import format_datetime
class CommissionResultXlsx(models.AbstractModel):
_name = "report.commission_simple.report_xlsx"
_inherit = "report.report_xlsx.abstract"
_description = "Commission Result XLSX"
def generate_xlsx_report(self, workbook, data, objects):
# for some strange reasons, lang is not kept in context
self = self.with_context(lang=self.env.user.lang)
result = objects[0]
sheet = workbook.add_worksheet(result.date_range_id.name)
styles = self._prepare_styles(workbook, result.company_id)
title = _("Commissions of %(partner)s for period %(period)s", partner=result.partner_id.name, period=result.date_range_id.name)
now_str = format_datetime(self.env, datetime.now())
i = 0
sheet.write(i, 0, title, styles['title'])
sheet.write(i, 5, _('Generated from Odoo on %s by %s') % (now_str, self.env.user.name), styles['regular_small'])
i += 1
sheet.write(i, 0, _('Start Date'), styles['subtitle'])
sheet.write(i, 1, result.date_start, styles['subtitle_date'])
i += 1
sheet.write(i, 0, _('End Date'), styles['subtitle'])
sheet.write(i, 1, result.date_end, styles['subtitle_date'])
i += 1
sheet.write(i, 0, _('Currency'), styles['subtitle'])
sheet.write(i, 1, result.company_id.currency_id.name, styles['subtitle'])
i += 1
sheet.write(i, 0, _('Base Total'), styles['subtitle'])
sheet.write(i, 1, result.base_total, styles['subtitle_amount'])
i += 1
sheet.write(i, 0, _('Amount Total'), styles['subtitle'])
sheet.write(i, 1, result.amount_total, styles['subtitle_amount'])
i += 3
cols = self._prepare_xlsx_cols()
coldict = {}
pos = 0
for key, label, width, style_suffix in cols:
coldict[key] = {
"label": label,
"width": width,
"pos": pos,
"style": style_suffix and f"regular_{style_suffix}" or "regular",
}
pos += 1
# header
for col_key, col_vals in coldict.items():
sheet.write(i, col_vals['pos'], col_vals['label'], styles['col_title'])
sheet.set_column(col_vals['pos'], col_vals['pos'], col_vals['width'])
# table content
for line in result._prepare_xlsx_lines():
i += 1
for col_key, value in line._prepare_commission_xlsx().items():
sheet.write(i, coldict[col_key]["pos"], value, styles[coldict[col_key]["style"]])
def _prepare_xlsx_cols(self):
cols = [ # key, label, width, style_suffix
("inv.name", _("Invoice"), 14, False),
("inv.date", _("Invoice Date"), 11, "date"),
("inv.partner", _("Customer"), 50, False),
("product", _("Product"), 35, False),
("qty", _("Quantity"), 8, "qty"),
("uom", _("Unit"), 8, False),
("commission_base", _("Commission Base"), 14, "amount"),
("commission_rate", _("Commission Rate"), 10, "rate"),
("commission_amount", _("Commission Amount"), 14, "amount"),
]
return cols
def _prepare_styles(self, workbook, company):
col_title_bg_color = '#eeeeee'
prec_qty = self.env['decimal.precision'].precision_get('Product Unit of Measure')
prec_rate = self.env['decimal.precision'].precision_get('Commission Rate')
prec_price = self.env['decimal.precision'].precision_get('Product Price')
regular_font_size = 10
date_format = "dd/mm/yyyy" # TODO depend on lang
num_format_amount = f"# ##0.{'0' * company.currency_id.decimal_places}"
num_format_qty = f"# ##0.{'0' * prec_qty}"
num_format_rate = f"""0.{'0' * prec_rate} " "%"""
num_format_price = f"# ##0.{'0' * prec_price}"
styles = {
'title': workbook.add_format({
'bold': True, 'font_size': regular_font_size + 10,
'font_color': '#003b6f'}),
'subtitle': workbook.add_format({
'bold': True, 'font_size': regular_font_size}),
'subtitle_date': workbook.add_format({
'bold': True, 'font_size': regular_font_size, 'num_format': date_format}),
'subtitle_amount': workbook.add_format({
'bold': True, 'font_size': regular_font_size, 'num_format': num_format_amount}),
'col_title': workbook.add_format({
'bold': True, 'bg_color': col_title_bg_color,
'text_wrap': True, 'font_size': regular_font_size,
'align': 'center',
}),
'regular_date': workbook.add_format({'num_format': date_format}),
'regular_amount': workbook.add_format({'num_format': num_format_amount}),
'regular_rate': workbook.add_format({'num_format': num_format_rate}),
'regular_qty': workbook.add_format({'num_format': num_format_qty}),
'regular_price': workbook.add_format({'num_format': num_format_price}),
'regular': workbook.add_format({}),
'regular_small': workbook.add_format({'font_size': regular_font_size - 2}),
}
return styles

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2025 Akretion France (https://www.akretion.com/)
@author: Alexis de Lattre <alexis.delattre@akretion.com>
The licence is in the file __manifest__.py
-->
<odoo>
<record id="commission_result_xlsx_report" model="ir.actions.report">
<field name="name">Détails Excel</field>
<field name="model">commission.result</field>
<field name="report_type">xlsx</field>
<field name="report_name">commission_simple.report_xlsx</field>
<field name="report_file">commission_simple.report_xlsx</field>
<field name="print_report_name">'commission-%s-%s' % (object.date_range_id.name.replace(' ', '_'), object.partner_id.name.replace(' ', '_'))</field>
<field name="binding_model_id" ref="model_commission_result" />
</record>
</odoo>

View File

@@ -8,4 +8,4 @@ access_commission_rule_audit,Read access on commission.rule for viewer group,mod
access_commission_result_full,Full access on commission.result to accountant,model_commission_result,account.group_account_user,1,1,1,1 access_commission_result_full,Full access on commission.result to accountant,model_commission_result,account.group_account_user,1,1,1,1
access_commission_result_read,Read access on commission.result to invoicing grp,model_commission_result,account.group_account_invoice,1,0,0,0 access_commission_result_read,Read access on commission.result to invoicing grp,model_commission_result,account.group_account_invoice,1,0,0,0
access_commission_result_audit,Read access on commission.result to viewer grp,model_commission_result,account.group_account_readonly,1,0,0,0 access_commission_result_audit,Read access on commission.result to viewer grp,model_commission_result,account.group_account_readonly,1,0,0,0
access_commission_compute_full,Full access to wizard commission.compute,model_commission_compute,account.group_account_manager,1,1,1,1 access_commission_compute_full,Full access to wizard commission.compute to Accountant,model_commission_compute,account.group_account_user,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
8 access_commission_result_full Full access on commission.result to accountant model_commission_result account.group_account_user 1 1 1 1
9 access_commission_result_read Read access on commission.result to invoicing grp model_commission_result account.group_account_invoice 1 0 0 0
10 access_commission_result_audit Read access on commission.result to viewer grp model_commission_result account.group_account_readonly 1 0 0 0
11 access_commission_compute_full Full access to wizard commission.compute Full access to wizard commission.compute to Accountant model_commission_compute account.group_account_manager account.group_account_user 1 1 1 1

View File

@@ -13,7 +13,7 @@
<field name="inherit_id" ref="account.view_move_line_form"/> <field name="inherit_id" ref="account.view_move_line_form"/>
<field name="arch" type="xml"> <field name="arch" type="xml">
<notebook position="inside"> <notebook position="inside">
<page name="commission" string="Commission" attrs="{'invisible': [('display_type', '!=', 'product')]}"> <page name="commission" string="Commission" invisible="display_type != 'product'">
<group name="commission_grp"> <group name="commission_grp">
<field name="commission_base"/> <field name="commission_base"/>
<label for="commission_rate"/> <label for="commission_rate"/>

View File

@@ -1,206 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2019 Akretion France (http://www.akretion.com)
@author Alexis de Lattre <alexis.delattre@akretion.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
-->
<odoo>
<menuitem id="commission_root" name="Commissions" parent="account.menu_finance" sequence="11"/>
<menuitem id="commission_config_root" name="Commissions" parent="account.menu_finance_configuration" sequence="110"/>
<!-- PROFILE -->
<record id="commission_profile_form" model="ir.ui.view">
<field name="name">commission.profile.form</field>
<field name="model">commission.profile</field>
<field name="arch" type="xml">
<form>
<sheet>
<widget name="web_ribbon" title="Archived" bg_color="bg-danger" attrs="{'invisible': [('active', '=', True)]}"/>
<group name="main">
<field name="name"/>
<field name="active" invisible="1"/>
<field name="company_id" invisible="1"/>
<field name="company_id" groups="base.group_multi_company"/>
<field name="trigger_type"/>
</group>
<group name="lines" string="Rules">
<field name="line_ids" nolabel="1" colspan="2"/>
</group>
</sheet>
</form>
</field>
</record>
<record id="commission_profile_tree" model="ir.ui.view">
<field name="name">commission.profile.tree</field>
<field name="model">commission.profile</field>
<field name="arch" type="xml">
<tree>
<field name="sequence" widget="handle"/>
<field name="name" decoration-bf="1"/>
<field name="trigger_type" optional="show"/>
<field name="company_id" groups="base.group_multi_company"/>
</tree>
</field>
</record>
<record id="commission_profile_action" model="ir.actions.act_window">
<field name="name">Commission Profiles</field>
<field name="res_model">commission.profile</field>
<field name="view_mode">tree,form</field>
</record>
<menuitem id="commission_profile_menu" action="commission_profile_action" parent="commission_config_root" sequence="18"/>
<!-- RULE -->
<record id="commission_rule_form" model="ir.ui.view">
<field name="name">commission.rule.form</field>
<field name="model">commission.rule</field>
<field name="arch" type="xml">
<form>
<sheet>
<group name="main">
<field name="profile_id" invisible="not context.get('commission_rule_main_view')"/>
<field name="company_id" groups="base.group_multi_company"/>
<field name="company_id" invisible="1"/>
<field name="applied_on" widget="radio"/>
</group>
<group name="match" string="Match">
<field name="partner_ids" attrs="{'invisible': [('applied_on', 'not in', ('0_customer_product', '1_customer_product_category'))], 'required': [('applied_on', 'in', ('0_customer_product', '1_customer_product_category'))]}"/>
<field name="product_categ_ids" attrs="{'invisible': [('applied_on', 'not in', ('1_customer_product_category', '3_product_category'))], 'required': [('applied_on', 'in', ('1_customer_product_category', '3_product_category'))]}"/>
<field name="product_ids" attrs="{'invisible': [('applied_on', 'not in', ('0_customer_product', '2_product'))], 'required': [('applied_on', 'in', ('0_customer_product', '2_product'))]}"/>
<field name="date_start"/>
<field name="date_end"/>
</group>
<group name="compute" string="Compute">
<label for="rate"/>
<div name="rate">
<field name="rate" class="oe_inline"/> %
</div>
</group>
</sheet>
</form>
</field>
</record>
<record id="commission_rule_tree" model="ir.ui.view">
<field name="name">commission.rule.tree</field>
<field name="model">commission.rule</field>
<field name="arch" type="xml">
<tree>
<field name="profile_id" invisible="not context.get('commission_rule_main_view')"/>
<field name="applied_on"/>
<field name="date_start"/>
<field name="date_end"/>
<field name="rate" string="Rate (%)"/>
</tree>
</field>
</record>
<record id="commission_rule_search" model="ir.ui.view">
<field name="name">commission.rule.search</field>
<field name="model">commission.rule</field>
<field name="arch" type="xml">
<search>
<filter string="Archived" name="inactive" domain="[('active', '=', False)]"/>
<group name="groupby">
<filter name="profile_groupby" string="Profile" context="{'group_by': 'profile_id'}"/>
</group>
</search>
</field>
</record>
<record id="commission_rule_action" model="ir.actions.act_window">
<field name="name">Commission Rules</field>
<field name="res_model">commission.rule</field>
<field name="view_mode">tree,form</field>
<field name="context">{'commission_rule_main_view': True}</field>
</record>
<menuitem id="commission_rule_menu" action="commission_rule_action" parent="commission_config_root" sequence="20"/>
<!-- RESULT -->
<record id="commission_result_form" model="ir.ui.view">
<field name="name">commission.result.form</field>
<field name="model">commission.result</field>
<field name="arch" type="xml">
<form>
<group name="main">
<group name="main-left">
<field name="partner_id"/>
<field name="profile_id" groups="account.group_account_manager"/>
<field name="company_currency_id" invisible="1"/>
<field name="company_id" invisible="1"/>
<field name="company_id" groups="base.group_multi_company"/>
<field name="amount_total"/>
</group>
<group name="main-right">
<field name="date_range_id"/>
<field name="date_start"/>
<field name="date_end"/>
</group>
</group>
<group name="lines" string="Commission Lines">
<field nolabel="1" name="line_ids" colspan="2">
<tree>
<field name="move_id"/>
<field name="move_line_id"/>
<field name="base"/>
<field name="rate" string="Rate (%)"/>
<field name="amount" sum="1"/>
<field name="rule_id"/>
<field name="company_currency_id" invisible="1"/>
</tree>
</field>
</group>
</form>
</field>
</record>
<record id="commission_result_tree" model="ir.ui.view">
<field name="name">commission.result.tree</field>
<field name="model">commission.result</field>
<field name="arch" type="xml">
<tree>
<field name="date_range_id" optional="show"/>
<field name="date_start" optional="hide"/>
<field name="date_end" optional="hide"/>
<field name="partner_id"/>
<field name="profile_id" groups="account.group_account_manager"/>
<field name="company_currency_id" invisible="1"/>
<field name="company_id" groups="base.group_multi_company"/>
<field name="amount_total" sum="1" optional="show"/>
</tree>
</field>
</record>
<record id="commission_result_search" model="ir.ui.view">
<field name="name">commission.result.search</field>
<field name="model">commission.result</field>
<field name="arch" type="xml">
<search>
<field name="partner_id"/>
<field name="date_range_id"/>
<group name="groupby">
<filter name="partner_groupby" string="Salesman" context="{'group_by': 'partner_id'}"/>
<filter name="date_range_groupby" string="Period" context="{'group_by': 'date_range_id'}"/>
</group>
</search>
</field>
</record>
<record id="commission_result_action" model="ir.actions.act_window">
<field name="name">Commissions</field>
<field name="res_model">commission.result</field>
<field name="view_mode">tree,form</field>
</record>
<menuitem id="commission_result_menu" action="commission_result_action" parent="commission_root" sequence="10"/>
</odoo>

View File

@@ -16,12 +16,13 @@
<field name="arch" type="xml"> <field name="arch" type="xml">
<form> <form>
<sheet> <sheet>
<widget name="web_ribbon" title="Archived" bg_color="bg-danger" attrs="{'invisible': [('active', '=', True)]}"/> <widget name="web_ribbon" title="Archived" bg_color="bg-danger" invisible="active"/>
<group name="main"> <group name="main">
<group name="main-left"> <group name="main-left">
<field name="name"/> <field name="name"/>
<field name="active" invisible="1"/> <field name="active" invisible="1"/>
<field name="trigger_type" widget="radio"/> <field name="trigger_type" widget="radio"/>
<field name="date_range_type_id"/>
</group> </group>
<group name="main-right"> <group name="main-right">
<field name="company_id" invisible="1"/> <field name="company_id" invisible="1"/>
@@ -31,12 +32,12 @@
<notebook> <notebook>
<page name="assignments" string="Assignments"> <page name="assignments" string="Assignments">
<field name="assign_ids"> <field name="assign_ids">
<tree editable="bottom"> <list editable="bottom">
<field name="assign_type"/> <field name="assign_type"/>
<field name="user_id" attrs="{'required': [('assign_type', '=', 'user')], 'readonly': [('assign_type', '!=', 'user')]}"/> <field name="user_id" required="assign_type == 'user'" readonly="assign_type != 'user'"/>
<field name="company_id" groups="base.group_multi_company"/> <field name="company_id" groups="base.group_multi_company"/>
<field name="company_id" invisible="1"/> <field name="company_id" column_invisible="1"/>
</tree> </list>
</field> </field>
</page> </page>
<page name="rules" string="Rules"> <page name="rules" string="Rules">
@@ -48,23 +49,43 @@
</field> </field>
</record> </record>
<record id="commission_profile_tree" model="ir.ui.view"> <record id="commission_profile_list" model="ir.ui.view">
<field name="name">commission.profile.tree</field> <field name="name">commission.profile.list</field>
<field name="model">commission.profile</field> <field name="model">commission.profile</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<tree> <list>
<field name="sequence" widget="handle"/> <field name="sequence" widget="handle"/>
<field name="name" decoration-bf="1"/> <field name="name" decoration-bf="1"/>
<field name="trigger_type" optional="show"/> <field name="trigger_type" optional="show" widget="badge" decoration-info="trigger_type == 'invoice'" decoration-success="trigger_type == 'paid'" decoration-warning="trigger_type == 'in_payment'"/>
<field name="date_range_type_id" optional="show"/>
<field name="company_id" groups="base.group_multi_company"/> <field name="company_id" groups="base.group_multi_company"/>
</tree> </list>
</field>
</record>
<record id="commission_profile_search" model="ir.ui.view">
<field name="model">commission.profile</field>
<field name="arch" type="xml">
<search>
<field name="name"/>
<filter string="Invoiced" name="invoice" domain="[('trigger_type', '=', 'invoice')]"/>
<filter string="Paid" name="paid" domain="[('trigger_type', '=', 'paid')]"/>
<filter string="In Payment and Paid" name="in_payment" domain="[('trigger_type', '=', 'in_payment')]"/>
<separator/>
<filter string="Archived" name="inactive" domain="[('active', '=', False)]"/>
<separator/>
<group name="groupby">
<filter name="date_range_type_groupby" string="Commission Periodicity" context="{'group_by': 'date_range_type_id'}"/>
</group>
</search>
</field> </field>
</record> </record>
<record id="commission_profile_action" model="ir.actions.act_window"> <record id="commission_profile_action" model="ir.actions.act_window">
<field name="name">Commission Profiles</field> <field name="name">Commission Profiles</field>
<field name="path">commission-profile</field>
<field name="res_model">commission.profile</field> <field name="res_model">commission.profile</field>
<field name="view_mode">tree,form</field> <field name="view_mode">list,form</field>
</record> </record>
<menuitem id="commission_profile_menu" action="commission_profile_action" parent="commission_config_root" sequence="18"/> <menuitem id="commission_profile_menu" action="commission_profile_action" parent="commission_config_root" sequence="18"/>

View File

@@ -13,8 +13,9 @@
<field name="arch" type="xml"> <field name="arch" type="xml">
<form> <form>
<header> <header>
<button name="draft2done" type="object" states="draft" string="Confirm" class="btn-primary"/> <button name="draft2done" type="object" invisible="state != 'draft'" string="Confirm" class="btn-primary"/>
<button name="backtodraft" type="object" states="done" string="Back to Draft" confirm="Are you sure you want to go back to draft?"/> <button name="backtodraft" type="object" invisible="state != 'done'" string="Back to Draft" confirm="Are you sure you want to go back to draft?"/>
<button name="%(commission_simple.commission_result_xlsx_report)d" type="action" string="Excel Export"/>
<field name="state" widget="statusbar"/> <field name="state" widget="statusbar"/>
</header> </header>
<group name="main"> <group name="main">
@@ -23,6 +24,7 @@
<field name="date_range_id"/> <field name="date_range_id"/>
<field name="date_start"/> <field name="date_start"/>
<field name="date_end"/> <field name="date_end"/>
<field name="base_total"/>
<field name="amount_total"/> <field name="amount_total"/>
<field name="company_currency_id" invisible="1"/> <field name="company_currency_id" invisible="1"/>
<field name="company_id" invisible="1"/> <field name="company_id" invisible="1"/>
@@ -34,38 +36,45 @@
</group> </group>
</group> </group>
<group name="lines" string="Commission Lines"> <group name="lines" string="Commission Lines">
<field nolabel="1" name="line_ids" colspan="2"> <field nolabel="1" name="line_ids" colspan="2" widget="many2many">
<tree> <list>
<field name="move_id"/> <field name="move_id"/>
<field name="date" optional="hide"/> <field name="date" optional="hide"/>
<field name="partner_id"/> <field name="partner_id"/>
<field name="product_id"/> <field name="product_id"/>
<field name="product_categ_id" optional="hide"/> <field name="product_categ_id" optional="hide"/>
<field name="name" optional="hide"/> <field name="name" optional="hide"/>
<field name="quantity" optional="hide"/>
<field name="product_uom_id" optional="hide" groups="uom.group_uom"/>
<field name="price_unit" string="Price" optional="hide"/>
<field name="discount" string="Disc.%" optional="hide"/>
<field name="price_subtotal" optional="hide" string="Invoiced Amount"/> <field name="price_subtotal" optional="hide" string="Invoiced Amount"/>
<field name="commission_base"/> <field name="commission_base" sum="1"/>
<field name="commission_rate" string="Rate (%)"/> <field name="commission_rate" string="Rate (%)"/>
<field name="commission_amount" sum="1"/> <field name="commission_amount" sum="1"/>
<field name="commission_rule_id" optional="hide"/> <field name="commission_rule_id" optional="hide"/>
<field name="company_currency_id" invisible="1"/> <field name="company_currency_id" invisible="1"/>
<field name="currency_id" invisible="1"/> <field name="currency_id" invisible="1"/>
</tree> </list>
</field> </field>
</group> </group>
<div class="oe_chatter"> <chatter />
<field name="message_follower_ids" widget="mail_followers"/>
<field name="activity_ids" widget="mail_activity"/>
<field name="message_ids" widget="mail_thread"/>
</div>
</form> </form>
</field> </field>
</record> </record>
<record id="commission_result_tree" model="ir.ui.view"> <record id="commission_result_list" model="ir.ui.view">
<field name="name">commission.result.tree</field> <field name="name">commission.result.list</field>
<field name="model">commission.result</field> <field name="model">commission.result</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<tree> <list decoration-info="state == 'draft'">
<header>
<button
name="draft2done"
type="object"
string="Validate"
/>
</header>
<field name="date_range_id" optional="show"/> <field name="date_range_id" optional="show"/>
<field name="date_start" optional="hide"/> <field name="date_start" optional="hide"/>
<field name="date_end" optional="hide"/> <field name="date_end" optional="hide"/>
@@ -74,9 +83,10 @@
<field name="assign_type" optional="hide" widget="badge" decoration-warning="assign_type == 'user'"/> <field name="assign_type" optional="hide" widget="badge" decoration-warning="assign_type == 'user'"/>
<field name="company_currency_id" invisible="1"/> <field name="company_currency_id" invisible="1"/>
<field name="company_id" groups="base.group_multi_company"/> <field name="company_id" groups="base.group_multi_company"/>
<field name="base_total" sum="1" optional="hide"/>
<field name="amount_total" sum="1" optional="show"/> <field name="amount_total" sum="1" optional="show"/>
<field name="state" decoration-info="state == 'draft'" decoration-success="state == 'done'" widget="badge"/> <field name="state" decoration-info="state == 'draft'" decoration-success="state == 'done'" widget="badge"/>
</tree> </list>
</field> </field>
</record> </record>
@@ -103,8 +113,9 @@
<record id="commission_result_action" model="ir.actions.act_window"> <record id="commission_result_action" model="ir.actions.act_window">
<field name="name">Commissions</field> <field name="name">Commissions</field>
<field name="path">commission-result</field>
<field name="res_model">commission.result</field> <field name="res_model">commission.result</field>
<field name="view_mode">tree,form</field> <field name="view_mode">list,form</field>
</record> </record>
<menuitem id="commission_result_menu" action="commission_result_action" parent="commission_root" sequence="10"/> <menuitem id="commission_result_menu" action="commission_result_action" parent="commission_root" sequence="10"/>

View File

@@ -19,9 +19,9 @@
<field name="company_id" invisible="1"/> <field name="company_id" invisible="1"/>
</group> </group>
<group name="match" string="Match"> <group name="match" string="Match">
<field name="partner_ids" attrs="{'invisible': [('applied_on', 'not in', ('0_customer_product', '1_customer_product_category'))], 'required': [('applied_on', 'in', ('0_customer_product', '1_customer_product_category'))]}"/> <field name="partner_ids" invisible="applied_on not in ('0_customer_product', '1_customer_product_category')" required="applied_on in ('0_customer_product', '1_customer_product_category')"/>
<field name="product_categ_ids" attrs="{'invisible': [('applied_on', 'not in', ('1_customer_product_category', '3_product_category'))], 'required': [('applied_on', 'in', ('1_customer_product_category', '3_product_category'))]}"/> <field name="product_categ_ids" invisible="applied_on not in ('1_customer_product_category', '3_product_category')" required="applied_on in ('1_customer_product_category', '3_product_category')"/>
<field name="product_ids" attrs="{'invisible': [('applied_on', 'not in', ('0_customer_product', '2_product'))], 'required': [('applied_on', 'in', ('0_customer_product', '2_product'))]}"/> <field name="product_ids" invisible="applied_on not in ('0_customer_product', '2_product')" required="applied_on in ('0_customer_product', '2_product')"/>
<field name="date_start"/> <field name="date_start"/>
<field name="date_end"/> <field name="date_end"/>
</group> </group>
@@ -37,18 +37,19 @@
</field> </field>
</record> </record>
<record id="commission_rule_tree" model="ir.ui.view"> <record id="commission_rule_list" model="ir.ui.view">
<field name="name">commission.rule.tree</field> <field name="name">commission.rule.list</field>
<field name="model">commission.rule</field> <field name="model">commission.rule</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<tree> <list>
<field name="profile_id" invisible="not context.get('commission_rule_main_view')"/> <field name="profile_id" invisible="not context.get('commission_rule_main_view')"/>
<field name="applied_on"/> <field name="applied_on" widget="badge" decoration-danger="applied_on == '0_customer_product'" decoration-warning="applied_on == '1_customer_product_category'" decoration-info="applied_on == '2_product'" decoration-success="applied_on == '3_product_category'"/>
<field name="date_start"/> <field name="apply_description"/>
<field name="date_end"/> <field name="date_start" optional="show"/>
<field name="date_end" optional="show"/>
<field name="rate" string="Rate (%)"/> <field name="rate" string="Rate (%)"/>
<field name="base"/> <field name="base" widget="badge" decoration-success="base == 'invoiced'" decoration-warning="base == 'margin'"/>
</tree> </list>
</field> </field>
</record> </record>
@@ -68,8 +69,9 @@
<record id="commission_rule_action" model="ir.actions.act_window"> <record id="commission_rule_action" model="ir.actions.act_window">
<field name="name">Commission Rules</field> <field name="name">Commission Rules</field>
<field name="path">commission-rule</field>
<field name="res_model">commission.rule</field> <field name="res_model">commission.rule</field>
<field name="view_mode">tree,form</field> <field name="view_mode">list,form</field>
<field name="context">{'commission_rule_main_view': True}</field> <field name="context">{'commission_rule_main_view': True}</field>
</record> </record>

View File

@@ -1,37 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2019-2024 Akretion France (https://www.akretion.com)
@author: Alexis de Lattre <alexis.delattre@akretion.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->
<odoo>
<record id="res_config_settings_view_form" model="ir.ui.view">
<field name="name">commission.res.config.settings.form</field>
<field name="model">res.config.settings</field>
<field name="inherit_id" ref="account.res_config_settings_view_form" />
<field name="arch" type="xml">
<xpath expr="//div[@id='storno']" position="after">
<h2>Commissions</h2>
<div class="row mt16 o_settings_container" id="commission_simple">
<div class="col-12 col-lg-12 o_setting_box" id="commission_simple-settings">
<div class="o_setting_left_pane" />
<div class="o_setting_right_pane">
<div class="row" id="commission_date_range_type_id">
<label
for="commission_date_range_type_id"
class="col-md-5"
/>
<field name="commission_date_range_type_id" />
</div>
</div>
</div>
</div>
</xpath>
</field>
</record>
</odoo>

View File

@@ -1,36 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2019-2024 Akretion France (https://www.akretion.com)
@author Alexis de Lattre <alexis.delattre@akretion.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
-->
<odoo>
<record id="view_users_form" model="ir.ui.view">
<field name="name">commission.res.users.form</field>
<field name="model">res.users</field>
<field name="inherit_id" ref="base.view_users_form"/>
<field name="arch" type="xml">
<group name="preferences" position="after">
<group name="commission" string="Commission" groups="account.group_account_manager">
<field name="commission_profile_id"/>
</group>
</group>
</field>
</record>
<record id="view_users_tree" model="ir.ui.view">
<field name="name">commission.res.users.tree</field>
<field name="model">res.users</field>
<field name="inherit_id" ref="base.view_users_tree"/>
<field name="arch" type="xml">
<field name="login_date" position="after">
<field name="commission_profile_id" optional="hide" groups="account.group_account_manager"/>
</field>
</field>
</record>
</odoo>

View File

@@ -1,2 +1 @@
from . import commission_compute from . import commission_compute
from . import res_config_settings

View File

@@ -3,8 +3,9 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import api, fields, models, _ from odoo import api, fields, models, _
from dateutil.relativedelta import relativedelta from datetime import datetime, timedelta
from odoo.exceptions import UserError from odoo.exceptions import UserError
from odoo.tools.misc import format_date
import logging import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -13,47 +14,44 @@ class CommissionCompute(models.TransientModel):
_name = 'commission.compute' _name = 'commission.compute'
_description = 'Compute Commissions' _description = 'Compute Commissions'
company_id = fields.Many2one('res.company', required=True, default=lambda self: self.env.company) company_id = fields.Many2one('res.company', required=True)
date_range_type_id = fields.Many2one(related='company_id.commission_date_range_type_id') date_start = fields.Date(string="Period Start Date", required=True)
date_range_id = fields.Many2one(
'date.range', required=True, string='Period',
compute='_compute_date_range_id', store=True, precompute=True, readonly=False,
domain="[('type_id', '=', date_range_type_id)]")
date_start = fields.Date(related='date_range_id.date_start')
date_end = fields.Date(related='date_range_id.date_end')
@api.depends('company_id') @api.model
def _compute_date_range_id(self): def default_get(self, fields_list):
for wiz in self: res = super().default_get(fields_list)
date_range_id = False company = self.env.company
company = wiz.company_id last_commission_result = self.env['commission.result'].search([
if company and company.commission_date_range_type_id: ('company_id', '=', company.id),
type_id = company.commission_date_range_type_id.id ], order='date_start desc', limit=1)
last_commission_result = self.env['commission.result'].search([ if last_commission_result:
('company_id', '=', company.id), last_start_date = last_commission_result.date_start
], order='date_end desc', limit=1) commissions_last_start_date = self.env['commission.result'].search([
limit_date = last_commission_result and last_commission_result.date_end or (fields.Date.context_today(self) + relativedelta(months=-2, day=31)) ('date_start', '=', last_start_date),
date_range = self.env['date.range'].search([ ('company_id', '=', company.id),
('company_id', 'in', (company.id, False)), ], order="date_end asc", limit=1)
('type_id', '=', type_id), min_end_date = commissions_last_start_date.date_end
('date_start', '>', limit_date) date_start = min_end_date + timedelta(1)
], order='date_start', limit=1) else:
date_range_id = date_range and date_range.id or False today = fields.Date.context_today(self)
wiz.date_range_id = date_range_id date_start = datetime(today.year, today.month, 1)
res.update({
'company_id': company.id,
'date_start': date_start,
})
return res
def run(self): def run(self):
self.ensure_one() self.ensure_one()
if not self.date_start:
raise UserError(_("Missing Period Start Date."))
creso = self.env['commission.result'] creso = self.env['commission.result']
date_range = self.date_range_id existing_commissions = creso.search_read([
existing_commissions = creso.search([ ('date_start', '=', self.date_start),
('date_range_id', '=', date_range.id),
('company_id', '=', self.company_id.id), ('company_id', '=', self.company_id.id),
]) ], ['assignment_id'])
if existing_commissions: exclude_assignment_ids = [x['assignment_id'][0] for x in existing_commissions if x['assignment_id']]
raise UserError(_( com_result_ids = self._core_compute(exclude_assignment_ids)
'Commissions already exist for %(period)s in company %(company)s.',
period=date_range.display_name, company=self.company_id.display_name))
com_result_ids = self._core_compute()
if not com_result_ids: if not com_result_ids:
raise UserError(_('No commissions generated.')) raise UserError(_('No commissions generated.'))
action = self.env['ir.actions.actions']._for_xml_id( action = self.env['ir.actions.actions']._for_xml_id(
@@ -64,12 +62,32 @@ class CommissionCompute(models.TransientModel):
}) })
return action return action
def _core_compute(self): def _core_compute(self, exclude_assignment_ids):
rules = self.env['commission.rule'].load_all_rules() rules = self.env['commission.rule'].load_all_rules()
com_result_ids = [] com_result_ids = []
assignments = self.env['commission.profile.assignment'].search([('company_id', '=', self.company_id.id)]) assignments = self.env['commission.profile.assignment'].search(
[('company_id', '=', self.company_id.id), ('id', 'not in', exclude_assignment_ids)])
date_range_type2date_range = {}
for assignment in assignments: for assignment in assignments:
com_result = assignment._generate_commission_result(self.date_range_id, rules) profile = assignment.profile_id
date_range_type = profile.date_range_type_id
if not date_range_type:
raise UserError(_("Missing commission periodicity on commission profile '%s'.") % profile.display_name)
if date_range_type not in date_range_type2date_range:
domain = [
('date_start', '=', self.date_start),
('type_id', '=', date_range_type.id),
]
date_range = self.env['date.range'].search(
domain + [('company_id', '=', self.company_id.id)], limit=1)
if not date_range:
date_range = self.env['date.range'].search(
domain + [('company_id', '=', False)], limit=1)
if not date_range:
logger.info('There is no date range with type %s starting on %s. Skipping commission generation for assignment ID %s', date_range_type.name, self.date_start, assignment.id)
continue
date_range_type2date_range[date_range_type] = date_range
com_result = assignment._generate_commission_result(date_range_type2date_range[date_range_type], rules)
if com_result: if com_result:
com_result_ids.append(com_result.id) com_result_ids.append(com_result.id)
else: else:

View File

@@ -15,10 +15,7 @@
<group name="main"> <group name="main">
<field name="company_id" groups="base.group_multi_company"/> <field name="company_id" groups="base.group_multi_company"/>
<field name="company_id" invisible="1"/> <field name="company_id" invisible="1"/>
<field name="date_range_type_id" invisible="1"/>
<field name="date_range_id"/>
<field name="date_start"/> <field name="date_start"/>
<field name="date_end"/>
</group> </group>
<footer> <footer>
<button name="run" type="object" string="Compute" <button name="run" type="object" string="Compute"
@@ -36,6 +33,6 @@
<field name="target">new</field> <field name="target">new</field>
</record> </record>
<menuitem id="commission_compute_menu" action="commission_compute_action" parent="commission_root" sequence="15" groups="account.group_account_user"/> <menuitem id="commission_compute_menu" action="commission_compute_action" parent="commission_root" sequence="15"/>
</odoo> </odoo>

View File

@@ -1,12 +0,0 @@
# Copyright 2019-2024 Akretion France (https://www.akretion.com/)
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import fields, models
class ResConfigSettings(models.TransientModel):
_inherit = 'res.config.settings'
commission_date_range_type_id = fields.Many2one(
related='company_id.commission_date_range_type_id', readonly=False)

View File

@@ -4,7 +4,7 @@
{ {
'name': 'Commission Simple Agent', 'name': 'Commission Simple Agent',
'version': '16.0.1.0.0', 'version': '18.0.1.0.0',
'category': 'Sales', 'category': 'Sales',
'license': 'AGPL-3', 'license': 'AGPL-3',
'summary': 'Glue module between commission_simple and sale_agent', 'summary': 'Glue module between commission_simple and sale_agent',
@@ -18,5 +18,5 @@
'views/commission_profile.xml', 'views/commission_profile.xml',
'views/commission_result.xml', 'views/commission_result.xml',
], ],
'installable': False, 'installable': True,
} }

View File

@@ -11,8 +11,8 @@
<field name="model">commission.profile</field> <field name="model">commission.profile</field>
<field name="inherit_id" ref="commission_simple.commission_profile_form"/> <field name="inherit_id" ref="commission_simple.commission_profile_form"/>
<field name="arch" type="xml"> <field name="arch" type="xml">
<xpath expr="//field[@name='assign_ids']/tree/field[@name='user_id']" position="after"> <xpath expr="//field[@name='assign_ids']/list/field[@name='user_id']" position="after">
<field name="agent_id" attrs="{'required': [('assign_type', '=', 'agent')], 'readonly': [('assign_type', '!=', 'agent')]}"/> <field name="agent_id" required="assign_type == 'agent'" readonly="assign_type != 'agent'" context="{'default_agent': True}"/>
</xpath> </xpath>
</field> </field>
</record> </record>

View File

@@ -1,18 +1,18 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- <!--
Copyright 2024 Akretion France (http://www.akretion.com) Copyright 2024 Akretion France (https://www.akretion.com)
@author Alexis de Lattre <alexis.delattre@akretion.com> @author Alexis de Lattre <alexis.delattre@akretion.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
--> -->
<odoo> <odoo>
<record id="commission_result_tree" model="ir.ui.view"> <record id="commission_result_list" model="ir.ui.view">
<field name="model">commission.result</field> <field name="model">commission.result</field>
<field name="inherit_id" ref="commission_simple.commission_result_tree"/> <field name="inherit_id" ref="commission_simple.commission_result_list"/>
<field name="arch" type="xml"> <field name="arch" type="xml">
<field name="assign_type" position="attributes"> <field name="assign_type" position="attributes">
<attribute name="decoration-danger">assign_type == 'agent'</attribute> <attribute name="decoration-danger">assign_type == 'agent'</attribute>
</field> </field>
</field> </field>
</record> </record>

View File

@@ -4,7 +4,7 @@
{ {
'name': 'Commission Simple Agent Purchase', 'name': 'Commission Simple Agent Purchase',
'version': '16.0.1.0.0', 'version': '18.0.1.0.0',
'category': 'Sales', 'category': 'Sales',
'license': 'AGPL-3', 'license': 'AGPL-3',
'summary': 'Glue module between commission_simple_agent and purchase', 'summary': 'Glue module between commission_simple_agent and purchase',
@@ -16,7 +16,8 @@
], ],
'data': [ 'data': [
'views/commission_result.xml', 'views/commission_result.xml',
'views/commission_profile.xml',
'wizards/res_config_settings.xml', 'wizards/res_config_settings.xml',
], ],
'installable': False, 'installable': True,
} }

View File

@@ -1,2 +1,3 @@
from . import commission_result from . import commission_result
from . import commission_profile
from . import res_company from . import res_company

View File

@@ -0,0 +1,20 @@
# Copyright Akretion France (http://www.akretion.com/)
# @author Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import fields, models, api, _
from odoo.exceptions import ValidationError
class CommissionProfile(models.Model):
_inherit = 'commission.profile'
commission_product_id = fields.Many2one(
'product.product', string='Specific Commission Product', ondelete='restrict',
check_company=True,
domain=[('type', '=', 'service')],
help="If not set, Odoo will use the commission product configured on the accounting "
"configuration page."
)

View File

@@ -5,6 +5,7 @@
from odoo import fields, models, _ from odoo import fields, models, _
from odoo.exceptions import UserError from odoo.exceptions import UserError
from odoo.tools.misc import format_amount, formatLang from odoo.tools.misc import format_amount, formatLang
from markupsafe import Markup
class CommissionResult(models.Model): class CommissionResult(models.Model):
@@ -14,26 +15,34 @@ class CommissionResult(models.Model):
def draft2done(self): def draft2done(self):
for result in self: for result in self:
if result.assign_type == 'agent': if result.state == "draft" and result.assign_type == 'agent':
if not result.purchase_id: if not result.purchase_id:
vals = result._prepare_purchase_order() vals = result._prepare_purchase_order()
po = self.env['purchase.order'].create(vals) po = self.env['purchase.order'].create(vals)
po.message_post(body=Markup(_("Generated from commission <a href=# data-oe-model=commission.result data-oe-id=%d>%s</a>.") % (result.id, result.display_name)))
result.write({'purchase_id': po.id}) result.write({'purchase_id': po.id})
else: else:
po = self.purchase_id po = self.purchase_id
if po.state in ('draft', 'sent', 'cancel'): if po.state in ('draft', 'sent', 'cancel'):
po.order_line.unlink() po.order_line.unlink()
po.message_post(body=Markup(_("Purchase order lines re-generated from commission <a href=# data-oe-model=commission.result data-oe-id=%d>%s</a>.") % (result.id, result.display_name)))
else: else:
raise UserError(_("Purchase Order %s has already been confirmed. You should cancel it first.") % po.display_name) raise UserError(_("Purchase Order %s has already been confirmed. You should cancel it first.") % po.display_name)
if po.state == 'cancel': if po.state == 'cancel':
po.button_draft() po.button_draft()
assert not po.order_line assert not po.order_line
# create lines # create lines
if not result.company_id.commission_product_id:
raise UserError(_("Commission product is not set on company %s.") % result.company_id.display_name)
line_vals = [] line_vals = []
for move_line in result.line_ids: if not result.company_id.commission_po_config:
line_vals.append(result._prepare_purchase_order_line(move_line, po)) raise UserError(_(
"Purchase order configuration for commission is not set on "
"the accounting configuration page of company '%s'.")
% result.company_id.display_name)
if result.company_id.commission_po_config == 'single_line':
line_vals.append(result._prepare_purchase_order_line_single_line(po))
else:
for move_line in result.line_ids:
line_vals.append(result._prepare_purchase_order_line(move_line, po))
po_lines = self.env['purchase.order.line'].create(line_vals) po_lines = self.env['purchase.order.line'].create(line_vals)
po_lines._compute_tax_id() po_lines._compute_tax_id()
return super().draft2done() return super().draft2done()
@@ -57,7 +66,13 @@ class CommissionResult(models.Model):
company_currency = move_line.company_id.currency_id company_currency = move_line.company_id.currency_id
lang = self.partner_id.lang or self.env.lang lang = self.partner_id.lang or self.env.lang
env = self.with_context(lang=lang).env env = self.with_context(lang=lang).env
product = self.company_id.commission_product_id product = self.profile_id.commission_product_id or self.company_id.commission_product_id
if not product:
raise UserError(_(
"Commission product is not set on profile '%(profile)s' "
"nor on company '%(company)s'.",
profile=self.profile_id.display_name,
company=self.company_id.display_name))
vals = { vals = {
'order_id': order.id, 'order_id': order.id,
'product_id': product.id, 'product_id': product.id,
@@ -68,6 +83,24 @@ class CommissionResult(models.Model):
} }
return vals return vals
def _prepare_purchase_order_line_single_line(self, order):
product = self.profile_id.commission_product_id or self.company_id.commission_product_id
if not product:
raise UserError(_(
"Commission product is not set on profile '%(profile)s' "
"nor on company '%(company)s'.",
profile=self.profile_id.display_name,
company=self.company_id.display_name))
vals = {
'order_id': order.id,
'product_id': product.id,
'name': _("Commissions for period %(period)s", period=self.date_range_id.name),
'product_qty': 1,
'product_uom': product.uom_id.id,
'price_unit': self.amount_total,
}
return vals
def unlink(self): def unlink(self):
for result in self: for result in self:
if result.purchase_id: if result.purchase_id:

View File

@@ -12,3 +12,7 @@ class ResCompany(models.Model):
commission_product_id = fields.Many2one( commission_product_id = fields.Many2one(
'product.product', string='Commission Product', ondelete='restrict', check_company=True, 'product.product', string='Commission Product', ondelete='restrict', check_company=True,
domain=[('type', '=', 'service')]) domain=[('type', '=', 'service')])
commission_po_config = fields.Selection([
('single_line', 'Single Line'),
('details', 'One line per commission line'),
], default='details', string="Purchase Order Configuration")

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2025 Akretion France (https://www.akretion.com)
@author Alexis de Lattre <alexis.delattre@akretion.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
-->
<odoo>
<record id="commission_profile_form" model="ir.ui.view">
<field name="model">commission.profile</field>
<field name="inherit_id" ref="commission_simple_agent.commission_profile_form"/>
<field name="arch" type="xml">
<group name="main-right" position="inside">
<field name="commission_product_id"/>
</group>
</field>
</record>
</odoo>

View File

@@ -12,7 +12,7 @@
<field name="inherit_id" ref="commission_simple.commission_result_form"/> <field name="inherit_id" ref="commission_simple.commission_result_form"/>
<field name="arch" type="xml"> <field name="arch" type="xml">
<group name="main-right" position="inside"> <group name="main-right" position="inside">
<field name="purchase_id" attrs="{'invisible': [('assign_type', '!=', 'agent')]}"/> <field name="purchase_id" invisible="assign_type != 'agent'"/>
</group> </group>
</field> </field>
</record> </record>

View File

@@ -10,3 +10,4 @@ class ResConfigSettings(models.TransientModel):
commission_product_id = fields.Many2one( commission_product_id = fields.Many2one(
related='company_id.commission_product_id', readonly=False) related='company_id.commission_product_id', readonly=False)
commission_po_config = fields.Selection(related="company_id.commission_po_config", readonly=False)

View File

@@ -11,13 +11,17 @@
<record id="res_config_settings_view_form" model="ir.ui.view"> <record id="res_config_settings_view_form" model="ir.ui.view">
<field name="name">commission.res.config.settings.form</field> <field name="name">commission.res.config.settings.form</field>
<field name="model">res.config.settings</field> <field name="model">res.config.settings</field>
<field name="inherit_id" ref="commission_simple.res_config_settings_view_form" /> <field name="inherit_id" ref="account.res_config_settings_view_form" />
<field name="arch" type="xml"> <field name="arch" type="xml">
<xpath expr="//div[@id='commission_simple-settings']/div[hasclass('o_setting_right_pane')]" position="inside"> <xpath expr="//block[@id='analytic']" position="after">
<div class="row" id="commission_product_id"> <block title="Commissions" id="commission_simple">
<label for="commission_product_id" class="col-md-5" /> <setting id="commission_product" title="Commission Product">
<field name="commission_product_id" context="{'default_detailed_type': 'service', 'default_purchase_ok': True, 'default_sale_ok': False, 'default_available_in_pos': False, 'default_purchase_method': 'purchase'}"/> <field name="commission_product_id" context="{'default_type': 'service', 'default_purchase_ok': True, 'default_sale_ok': False, 'default_available_in_pos': False, 'default_purchase_method': 'purchase'}"/>
</div> </setting>
<setting id="commission_po_config" title="Purchase Order">
<field name="commission_po_config" />
</setting>
</block>
</xpath> </xpath>
</field> </field>
</record> </record>

View File

@@ -1,10 +1,10 @@
# Copyright 2016-2022 Akretion (http://www.akretion.com) # Copyright 2016-2025 Akretion France (https://www.akretion.com)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
# @author Alexis de Lattre <alexis.delattre@akretion.com> # @author Alexis de Lattre <alexis.delattre@akretion.com>
{ {
'name': 'CRM Usability', 'name': 'CRM Usability',
'version': '16.0.1.0.0', 'version': '18.0.1.0.0',
'category': 'Customer Relationship Management', 'category': 'Customer Relationship Management',
'license': 'AGPL-3', 'license': 'AGPL-3',
'summary': 'CRM usability enhancements', 'summary': 'CRM usability enhancements',
@@ -16,10 +16,10 @@ This module has been written by Alexis de Lattre from Akretion
<alexis.delattre@akretion.com>. <alexis.delattre@akretion.com>.
""", """,
'author': 'Akretion', 'author': 'Akretion',
'website': 'http://www.akretion.com', 'website': 'https://github.com/akretion/odoo-usability',
'depends': ['crm'], 'depends': ['crm'],
'data': [ 'data': [
'views/crm_lead.xml', 'views/crm_lead.xml',
], ],
'installable': False, 'installable': True,
} }

View File

@@ -0,0 +1,3 @@
[build-system]
requires = ["whool"]
build-backend = "whool.buildapi"

View File

@@ -3,7 +3,7 @@
{ {
'name': 'Eradicate Quick Create', 'name': 'Eradicate Quick Create',
'version': '16.0.1.0.0', 'version': '18.0.1.0.0',
'category': 'Tools', 'category': 'Tools',
'license': 'AGPL-3', 'license': 'AGPL-3',
'summary': 'Disable quick create on all objects', 'summary': 'Disable quick create on all objects',
@@ -18,8 +18,8 @@ This module uses the module *web_m2x_options* from the OCA *web* project (in v10
This module has been written by Alexis de Lattre from Akretion <alexis.delattre@akretion.com>. This module has been written by Alexis de Lattre from Akretion <alexis.delattre@akretion.com>.
""", """,
'author': 'Akretion', 'author': 'Akretion',
'website': 'http://www.akretion.com', 'website': 'https://github.com/akretion/odoo-usability',
'depends': ['web_m2x_options'], 'depends': ['web_m2x_options'],
'post_init_hook': 'web_m2x_options_create', 'post_init_hook': 'web_m2x_options_create',
'installable': False, 'installable': True,
} }

View File

@@ -2,12 +2,8 @@
# @author: Alexis de Lattre <alexis.delattre@akretion.com> # @author: Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import SUPERUSER_ID
from odoo.api import Environment
def web_m2x_options_create(env):
def web_m2x_options_create(cr, registry):
env = Environment(cr, SUPERUSER_ID, {})
config_parameter = env['ir.config_parameter'].search( config_parameter = env['ir.config_parameter'].search(
[('key', '=', 'web_m2x_options.create')]) [('key', '=', 'web_m2x_options.create')])
if config_parameter and config_parameter.value != 'False': if config_parameter and config_parameter.value != 'False':

View File

@@ -0,0 +1,3 @@
[build-system]
requires = ["whool"]
build-backend = "whool.buildapi"

View File

@@ -0,0 +1,100 @@
# Copyright 2025 Akretion France (https://www.akretion.com)
# @author Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{
'name': 'Account Profile for France',
'version': '18.0.1.0.0',
'category': 'Accounting & Finance',
'license': 'AGPL-3',
'summary': 'Module set for accounting for a French company',
'author': 'Akretion',
'website': 'https://github.com/akretion/odoo-usability',
'depends': [
### MISC
'date_range_account', # OCA/server-ux
'account_usability_akretion', # akretion/odoo-usability
'account_usability', # OCA/account-financial-tools
'l10n_fr_siret_lookup', # OCA/l10n-france
'account_payment_base_oca', # OCA/bank-payment-alternative
'account_move_name_sequence', # OCA/account-financial-tools
'account_lock_date_update', # OCA/account-financial-tools
'account_move_csv_import', # akretion/account-move-import
'account_move_line_reconcile_manual', # OCA/account-reconcile
'account_dashboard_banner', # OCA/account-financial-tools
### INVOICING
'account_invoice_fiscal_position_update', # OCA/account-invoicing
'account_fiscal_position_vat_check', # OCA/account-financial-tools
'account_invoice_facturx', # OCA/edi
### FINANCIAL REPORTS
'account_financial_report', # OCA/account-financial-reporting
'account_balance_ebp_csv_export', # OCA/l10n-france
# 'l10n_fr_mis_reports', # OCA/l10n-france
# 'l10n_fr_fec_oca', # OCA/l10n-france
### BANK STATEMENTS
'account_statement_completion_label_simple', # akretion/bank-statement-reconcile-simple
#'account_statement_completion_label_simple_sale', # akretion/bank-statement-reconcile-simple
'account_statement_import_file_reconcile_oca', # OCA/bank-statement-import
'account_statement_import_ofx', # OCA/bank-statement-import
'account_statement_import_fr_cfonb', # OCA/l10n-france
# 'account_statement_import_api_qonto', # akretion/bank-statement-import-api
'account_reconcile_oca', # OCA/account-reconcile
### CURRENCY RATES
'currency_rate_update', # OCA/currency
'currency_old_rate_notify', # OCA/currency
### INVOICE IMPORT
#'account_invoice_import_simple_pdf', # OCA/edi
#'account_invoice_import_facturx', # OCA/edi
#'l10n_fr_account_invoice_import_facturx', # OCA/l10n-france
### OVERDUE
'account_invoice_overdue_warn', # OCA/credit-control
#'account_invoice_overdue_warn_sale', # OCA/credit-control
'account_invoice_overdue_reminder', # OCA/credit-control
### FRENCH DECLARATIONS
'l10n_fr_account_vat_return_teledec', # OCA/l10n-france
'l10n_fr_account_vat_return_einvoice_generate', # OCA/l10n-france
# 'intrastat_product' depends on 'sale_stock' and 'purchase_stock', so I don't add
# as a depend on this module
# 'l10n_fr_intrastat_product', # OCA/l10n-france
# 'product_net_weight', # OCA/product-attribute
'l10n_fr_intrastat_service', # OCA/l10n-france
'l10n_fr_das2', # OCA/l10n-france
# ANALYTIC
#'account_analytic_distribution_manual', # OCA/account-analytic
### INVOICE IMPORT
#'account_invoice_download_ovh', # OCA/edi
#'account_invoice_download_scaleway', # OCA/edi
### PAYMENT ORDERS and DEBIT ORDERS
#'partner_bank_acc_type_constraint', # OCA/partner-contact
#'account_payment_base_oca_sale', # OCA/bank-payment-alternative
#'account_payment_sepa_credit_transfer', # OCA/bank-payment-alternative
#'account_payment_sepa_direct_debit', # OCA/bank-payment-alternative
#'account_payment_batch_oca_reconcile', # OCA/bank-payment-alternative
#'account_payment_fr_lcr', # OCA/l10n-france
#'l10n_fr_account_payment_intl_credit_transfer', # OCA/l10n-france
### PY3O
#'account_invoice_facturx_py3o', # OCA/edi
### CHORUS
#'l10n_fr_chorus_account', # OCA/l10n-france
#'l10n_fr_chorus_sale', # OCA/l10n-france
#'l10n_fr_chorus_facturx', # OCA/l10n-france
### CUTOFF
#'account_cutoff_start_end_dates', # OCA/account-closing
#'account_cutoff_picking', # OCA/account-closing
#'account_cutoff_accrual_subscription', # OCA/account-closing
### MISC
#'account_check_deposit', # OCA/account-financial-tools
#'account_cash_deposit', # OCA/account-financial-tools
#'account_invoice_pricelist', # OCA/account-invoicing
#'account_asset_management', # OCA/account-financial-tools
### MOONCARD
#'mooncard_payment_card', # akretion/odoo-mooncard-connector
#'l10n_fr_base_newgen_payment_card', # akretion/odoo-mooncard-connector
#'base_newgen_payment_card_start_end_dates', # akretion/odoo-mooncard-connector
],
'excludes': [
#'l10n_fr_fec',
'account_edi_ubl_cii',
],
'installable': True,
}

View File

@@ -0,0 +1,3 @@
[build-system]
requires = ["whool"]
build-backend = "whool.buildapi"

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

View File

@@ -5,7 +5,7 @@
{ {
'name': 'Mail Usability', 'name': 'Mail Usability',
'version': '16.0.1.0.0', 'version': '18.0.1.0.0',
'category': 'Productivity/Discuss', 'category': 'Productivity/Discuss',
'license': 'AGPL-3', 'license': 'AGPL-3',
'summary': 'Usability improvements on mails', 'summary': 'Usability improvements on mails',
@@ -20,13 +20,8 @@ Small usability improvements on mails:
* remove 'sent by' in notification footer (TODO mig v14) * remove 'sent by' in notification footer (TODO mig v14)
""", """,
'author': 'Akretion', 'author': 'Akretion',
'website': 'http://www.akretion.com', 'website': 'https://github.com/akretion/odoo-usability',
'depends': ['mail'], 'depends': ['mail'],
'data': [ 'data': [],
#'views/mail_view.xml', 'installable': True,
#'data/mail_data.xml',
#'wizard/email_template_preview_view.xml',
#'wizard/mail_compose_message_view.xml',
],
'installable': False,
} }

View File

@@ -9,8 +9,7 @@ class ResPartner(models.Model):
_inherit = 'res.partner' _inherit = 'res.partner'
# tracking=True is handled in the 'mail' module, so it's better # tracking=True is handled in the 'mail' module, so it's better
# to have this in mail_usability than in base_usability # to have this in mail_usability than in base_usability_akretion
name = fields.Char(tracking=True)
ref = fields.Char(tracking=True) ref = fields.Char(tracking=True)
lang = fields.Selection(tracking=True) lang = fields.Selection(tracking=True)
street = fields.Char(tracking=True) street = fields.Char(tracking=True)

View File

@@ -0,0 +1,3 @@
[build-system]
requires = ["whool"]
build-backend = "whool.buildapi"

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

View File

@@ -14,7 +14,7 @@
<field name="inherit_id" ref="product.product_template_search_view" /> <field name="inherit_id" ref="product.product_template_search_view" />
<field name="arch" type="xml"> <field name="arch" type="xml">
<field name="categ_id" position="after"> <field name="categ_id" position="after">
<field name="seller_id" string="Main Supplier"/> <field name="seller_id" domain="[('parent_id', '=', False)]"/>
</field> </field>
</field> </field>
</record> </record>

View File

@@ -4,7 +4,7 @@
{ {
'name': 'Sale Agent', 'name': 'Sale Agent',
'version': '16.0.1.0.0', 'version': '18.0.1.0.0',
'category': 'Sales', 'category': 'Sales',
'license': 'AGPL-3', 'license': 'AGPL-3',
'summary': 'Add agent on partner, sale order and customer invoice/refund', 'summary': 'Add agent on partner, sale order and customer invoice/refund',
@@ -19,5 +19,5 @@
"views/account_invoice_report.xml", "views/account_invoice_report.xml",
], ],
'demo': ['demo/demo.xml'], 'demo': ['demo/demo.xml'],
'installable': False, 'installable': True,
} }

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- <!--
Copyright 2020 Akretion France (http://www.akretion.com/) Copyright 2024-2025 Akretion France (https://www.akretion.com/)
@author: Alexis de Lattre <alexis.delattre@akretion.com> @author: Alexis de Lattre <alexis.delattre@akretion.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
--> -->

View File

@@ -3,6 +3,7 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import fields, models from odoo import fields, models
from odoo.tools import SQL
class AccountInvoiceReport(models.Model): class AccountInvoiceReport(models.Model):
@@ -11,5 +12,4 @@ class AccountInvoiceReport(models.Model):
invoice_agent_id = fields.Many2one("res.partner", string="Agent", readonly=True) invoice_agent_id = fields.Many2one("res.partner", string="Agent", readonly=True)
def _select(self): def _select(self):
select_str = super()._select() return SQL("%s, move.invoice_agent_id AS invoice_agent_id", super()._select())
return f"{select_str}, move.invoice_agent_id AS invoice_agent_id"

View File

@@ -14,7 +14,7 @@
<field name="inherit_id" ref="base.view_partner_form"/> <field name="inherit_id" ref="base.view_partner_form"/>
<field name="arch" type="xml"> <field name="arch" type="xml">
<xpath expr="//page[@name='sales_purchases']//group[@name='sale']/field[@name='user_id']" name="user_id" position="after"> <xpath expr="//page[@name='sales_purchases']//group[@name='sale']/field[@name='user_id']" name="user_id" position="after">
<field name="agent_id" attrs="{'invisible': [('parent_id', '!=', False)]}"/> <field name="agent_id" invisible="parent_id"/>
</xpath> </xpath>
<group name="misc" position="inside"> <group name="misc" position="inside">
<field name="agent"/> <field name="agent"/>

View File

@@ -1,10 +1,18 @@
[project] [project]
name = "odoo-addons-akretion-odoo-usability" name = "odoo-addons-akretion-odoo-usability"
version = "18.0.20250130.0" version = "18.0.20251107.0"
dependencies = [ dependencies = [
"odoo-addon-account_invoice_update_wizard==18.0.*",
"odoo-addon-account_usability_akretion==18.0.*", "odoo-addon-account_usability_akretion==18.0.*",
"odoo-addon-base_company_extension==18.0.*",
"odoo-addon-base_mail_sender_bcc==18.0.*",
"odoo-addon-base_partner_ref==18.0.*", "odoo-addon-base_partner_ref==18.0.*",
"odoo-addon-base_profile_akretion==18.0.*",
"odoo-addon-base_usability_akretion==18.0.*", "odoo-addon-base_usability_akretion==18.0.*",
"odoo-addon-crm_usability_akretion==18.0.*",
"odoo-addon-eradicate_quick_create==18.0.*",
"odoo-addon-l10n_fr_account_profile_akretion==18.0.*",
"odoo-addon-mail_usability_akretion==18.0.*",
"odoo-addon-mrp_usability_akretion==18.0.*", "odoo-addon-mrp_usability_akretion==18.0.*",
"odoo-addon-partner_tree_default_account==18.0.*", "odoo-addon-partner_tree_default_account==18.0.*",
"odoo-addon-partner_tree_default_base==18.0.*", "odoo-addon-partner_tree_default_base==18.0.*",

View File

@@ -6,8 +6,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: Odoo Server 16.0\n" "Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-02-13 21:59+0000\n" "POT-Creation-Date: 2025-06-23 09:19+0000\n"
"PO-Revision-Date: 2023-02-13 21:59+0000\n" "PO-Revision-Date: 2025-06-23 09:19+0000\n"
"Last-Translator: \n" "Last-Translator: \n"
"Language-Team: \n" "Language-Team: \n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
@@ -109,6 +109,11 @@ msgstr "Lot/numéro de série"
msgid "Move" msgid "Move"
msgstr "Déplacer" msgstr "Déplacer"
#. module: stock_quant_package_move_wizard
#: model_terms:ir.ui.view,arch_db:stock_quant_package_move_wizard.stock_quant_move_wizard_form
msgid "Move and Show Picking"
msgstr "Déplacer et afficher le bon de transfert"
#. module: stock_quant_package_move_wizard #. module: stock_quant_package_move_wizard
#: model:ir.actions.act_window,name:stock_quant_package_move_wizard.stock_quant_move_wizard_action #: model:ir.actions.act_window,name:stock_quant_package_move_wizard.stock_quant_move_wizard_action
msgid "Move to Another Location" msgid "Move to Another Location"
@@ -137,7 +142,7 @@ msgstr "Qté à déplacer"
#. module: stock_quant_package_move_wizard #. module: stock_quant_package_move_wizard
#: model:ir.model.fields,field_description:stock_quant_package_move_wizard.field_stock_quant_move_wizard_line__quant_id #: model:ir.model.fields,field_description:stock_quant_package_move_wizard.field_stock_quant_move_wizard_line__quant_id
msgid "Quant" msgid "Quant"
msgstr "Quant" msgstr ""
#. module: stock_quant_package_move_wizard #. module: stock_quant_package_move_wizard
#: model:ir.model.fields,field_description:stock_quant_package_move_wizard.field_stock_quant_move_wizard_line__wizard_id #: model:ir.model.fields,field_description:stock_quant_package_move_wizard.field_stock_quant_move_wizard_line__wizard_id
@@ -154,7 +159,7 @@ msgstr ""
#. module: stock_quant_package_move_wizard #. module: stock_quant_package_move_wizard
#: model:ir.model,name:stock_quant_package_move_wizard.model_stock_quant #: model:ir.model,name:stock_quant_package_move_wizard.model_stock_quant
msgid "Quants" msgid "Quants"
msgstr "Quants" msgstr ""
#. module: stock_quant_package_move_wizard #. module: stock_quant_package_move_wizard
#: model:ir.model.fields,field_description:stock_quant_package_move_wizard.field_stock_quant_move_wizard__origin #: model:ir.model.fields,field_description:stock_quant_package_move_wizard.field_stock_quant_move_wizard__origin
@@ -196,7 +201,8 @@ msgid ""
"%(product_name)s to %(dest_location)s, but it is already on that location!" "%(product_name)s to %(dest_location)s, but it is already on that location!"
msgstr "" msgstr ""
"Vous essayez de déplacer %(qty)s %(uom)s d'un quant de l'article " "Vous essayez de déplacer %(qty)s %(uom)s d'un quant de l'article "
"%(product_name)s vers %(dest_location)s, mais il est déjà sur cet emplacement !" "%(product_name)s vers %(dest_location)s, mais il est déjà sur cet "
"emplacement !"
#. module: stock_quant_package_move_wizard #. module: stock_quant_package_move_wizard
#. odoo-python #. odoo-python

View File

@@ -6,8 +6,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: Odoo Server 16.0\n" "Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-02-13 21:58+0000\n" "POT-Creation-Date: 2025-06-23 09:19+0000\n"
"PO-Revision-Date: 2023-02-13 21:58+0000\n" "PO-Revision-Date: 2025-06-23 09:19+0000\n"
"Last-Translator: \n" "Last-Translator: \n"
"Language-Team: \n" "Language-Team: \n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
@@ -109,6 +109,11 @@ msgstr ""
msgid "Move" msgid "Move"
msgstr "" msgstr ""
#. module: stock_quant_package_move_wizard
#: model_terms:ir.ui.view,arch_db:stock_quant_package_move_wizard.stock_quant_move_wizard_form
msgid "Move and Show Picking"
msgstr ""
#. module: stock_quant_package_move_wizard #. module: stock_quant_package_move_wizard
#: model:ir.actions.act_window,name:stock_quant_package_move_wizard.stock_quant_move_wizard_action #: model:ir.actions.act_window,name:stock_quant_package_move_wizard.stock_quant_move_wizard_action
msgid "Move to Another Location" msgid "Move to Another Location"

View File

@@ -100,6 +100,11 @@ class StockQuant(models.Model):
"location_dest_id": dest_location.id, "location_dest_id": dest_location.id,
"origin": origin, "origin": origin,
} }
source_loc_ids = set()
for quant in self:
source_loc_ids.add(quant.location_id.id)
if len(source_loc_ids) == 1:
vals['location_id'] = list(source_loc_ids)[0]
return vals return vals
def move_full_quant_to(self, dest_location, picking_type=False, origin=False): def move_full_quant_to(self, dest_location, picking_type=False, origin=False):

View File

@@ -60,22 +60,9 @@ class StockQuantMoveWizard(models.TransientModel):
def run(self): def run(self):
self.ensure_one() self.ensure_one()
picking_id = False res = self.line_ids.quant_id.move_full_quant_to(
if self.picking_type_id: self.location_dest_id, self.picking_type_id, origin=self.origin)
picking_vals = self.env["stock.quant"]._prepare_move_to_stock_picking( picking_id = res['picking_id']
self.location_dest_id, self.picking_type_id, origin=self.origin
)
picking_id = self.env["stock.picking"].create(picking_vals).id
smo = self.env["stock.move"]
for line in self.line_ids:
quant = line.quant_id
assert not quant.package_id
vals = quant._prepare_move_to_stock_move(
line.quantity, self.location_dest_id, picking_id, origin=self.origin
)
new_move = smo.create(vals)
new_move._action_done()
assert new_move.state == "done"
action = {} action = {}
if picking_id and self._context.get("run_show_picking"): if picking_id and self._context.get("run_show_picking"):
action = self.env["ir.actions.actions"]._for_xml_id( action = self.env["ir.actions.actions"]._for_xml_id(

View File

@@ -35,7 +35,8 @@
</field> </field>
</group> </group>
<footer> <footer>
<button name="run" string="Move" type="object" class="btn-primary" /> <button name="run" string="Move and Show Picking" type="object" class="btn-primary" context="{'run_show_picking': True}" />
<button name="run" string="Move" type="object" />
<button string="Cancel" special="cancel" /> <button string="Cancel" special="cancel" />
</footer> </footer>
</form> </form>

View File

@@ -15,6 +15,23 @@ class StockWarehouseOrderpoint(models.Model):
# but all the Odoo deployments I've seen so far need 'manual' by default # but all the Odoo deployments I've seen so far need 'manual' by default
trigger = fields.Selection(default='manual') trigger = fields.Selection(default='manual')
product_barcode = fields.Char(related='product_id.barcode', string="Product Barcode") product_barcode = fields.Char(related='product_id.barcode', string="Product Barcode")
seller_id = fields.Many2one(
"res.partner",
compute="_compute_seller_id",
search="_search_seller_id",
string="Main Supplier")
def _search_seller_id(self, operator, value):
# searching on the first line of a o2m is not that easy
# So we search all potential matching products
# Then we filter on the seller_id
records = self.search([("product_id.seller_ids.partner_id", operator, value)])
records = records.filtered_domain([("seller_id", operator, value)])
return [("id", "in", records.ids)]
def _compute_seller_id(self):
for orderpoint in self:
orderpoint.seller_id = fields.first(orderpoint.product_id.seller_ids).partner_id
def _procure_orderpoint_confirm( def _procure_orderpoint_confirm(
self, use_new_cursor=False, company_id=None, raise_user_error=True): self, use_new_cursor=False, company_id=None, raise_user_error=True):

View File

@@ -47,6 +47,19 @@
</field> </field>
</record> </record>
<!-- View used by menu Inventory > Analysis > Locations -->
<!--
<record id="view_stock_quant_tree_editable" model="ir.ui.view">
<field name="model">stock.quant</field>
<field name="inherit_id" ref="stock.view_stock_quant_tree_editable"/>
<field name="arch" type="xml">
<field name="reserved_quantity" position="after">
<button type="object" name="action_stock_move_lines_reserved" string="Reservations" attrs="{'invisible': [('reserved_quantity', '=', 0)]}"/>
</field>
</field>
</record>
-->
<record id="quant_search_view" model="ir.ui.view"> <record id="quant_search_view" model="ir.ui.view">
<field name="model">stock.quant</field> <field name="model">stock.quant</field>
<field name="inherit_id" ref="stock.quant_search_view"/> <field name="inherit_id" ref="stock.quant_search_view"/>

View File

@@ -28,8 +28,20 @@
<field name="product_id" position="after"> <field name="product_id" position="after">
<field name="product_barcode" optional="hide"/> <field name="product_barcode" optional="hide"/>
</field> </field>
<field name="route_id" position="after">
<field name="seller_id" optional="show"/>
</field>
</field> </field>
</record> </record>
<record id="stock_reorder_report_search" model="ir.ui.view">
<field name="model">stock.warehouse.orderpoint</field>
<field name="inherit_id" ref="stock.stock_reorder_report_search"/>
<field name="arch" type="xml">
<field name="product_category_id" position="after">
<field name="seller_id" domain="[('parent_id', '=', False)]"/>
</field>
</field>
</record>
</odoo> </odoo>