diff --git a/l10n_jp_summary_invoice_carryover/README.rst b/l10n_jp_summary_invoice_carryover/README.rst new file mode 100644 index 00000000..0438d922 --- /dev/null +++ b/l10n_jp_summary_invoice_carryover/README.rst @@ -0,0 +1,154 @@ +.. image:: https://odoo-community.org/readme-banner-image + :target: https://odoo-community.org/get-involved?utm_source=readme + :alt: Odoo Community Association + +================================= +Japan Summary Invoice - Carryover +================================= + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:af66d58c97f76ee6a35503574d64f67f3c986cee13f5675efdfb3515c237640c + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Alpha-red.png + :target: https://odoo-community.org/page/development-status + :alt: Alpha +.. |badge2| image:: https://img.shields.io/badge/license-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fl10n--japan-lightgray.png?logo=github + :target: https://github.com/OCA/l10n-japan/tree/19.0/l10n_jp_summary_invoice_carryover + :alt: OCA/l10n-japan +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/l10n-japan-19-0/l10n-japan-19-0-l10n_jp_summary_invoice_carryover + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png + :target: https://runboat.odoo-community.org/builds?repo=OCA/l10n-japan&target_branch=19.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module extends Japan Summary Invoice to support carryover amount +tracking for recurring billing cycles. + +It adds the following fields to billings: + +- **Previous Billed Amount**: Total from the previous billing period +- **Payment Amount**: Payments received against the previous billing +- **Carryover Amount**: Outstanding balance carried forward +- **Total Billed Amount**: Carryover plus current purchases + +Manual override fields are available to adjust values when needed. + +The visibility of carryover amounts in the report can be controlled at +company, partner, and billing levels. + +.. IMPORTANT:: + This is an alpha version, the data model and design can change at any time without warning. + Only for development or testing purpose, do not use in production. + `More details on development status `_ + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +The visibility of carryover amounts in the report can be controlled at +three levels: + +1. **Company** (default for all partners): Go to *Settings > Invoicing > + Japan Summary Invoice* and enable/disable **Show Carryover Amounts**. + +2. **Partner** (override for specific customers): Go to the partner + form, under *Sales & Purchases > Summary Invoice*, set **Show + Carryover Amounts** to: + + - *Use Company Default*: Follow the company setting + - *Yes*: Always show carryover amounts + - *No*: Never show carryover amounts + +3. **Billing** (per-billing override): In the billing form under the + *Carryover* tab, the **Show Carryover Amounts** checkbox is computed + from the partner setting but can be manually adjusted. + +Usage +===== + +1. When creating a new billing, the system automatically finds the most + recent validated billing for the same partner and calculates: + + - **Previous Billed Amount**: Total amount from the previous + billing. + - **Payment Amount**: Payments received on the previous billing + invoices. + - **Carryover Amount**: Previous billed amount minus payments. + +2. Use the **Manual Adj.** toggle to manually override the computed + values if needed. This is useful when the previous billing was + created before this module was installed or when adjustments are + required. +3. When the billing is validated, the current computed values are + automatically frozen by enabling the manual override toggles. This + ensures that subsequent payments on previous invoices do not affect + the validated billing's carryover amounts. + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +------- + +* Quartile + +Contributors +------------ + +- `Quartile `__: + + - Yoshi Tashiro + - Aung Ko Ko Lin + +Maintainers +----------- + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +.. |maintainer-yostashiro| image:: https://github.com/yostashiro.png?size=40px + :target: https://github.com/yostashiro + :alt: yostashiro +.. |maintainer-aungkokolin1997| image:: https://github.com/aungkokolin1997.png?size=40px + :target: https://github.com/aungkokolin1997 + :alt: aungkokolin1997 + +Current `maintainers `__: + +|maintainer-yostashiro| |maintainer-aungkokolin1997| + +This module is part of the `OCA/l10n-japan `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/l10n_jp_summary_invoice_carryover/__init__.py b/l10n_jp_summary_invoice_carryover/__init__.py new file mode 100644 index 00000000..0650744f --- /dev/null +++ b/l10n_jp_summary_invoice_carryover/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/l10n_jp_summary_invoice_carryover/__manifest__.py b/l10n_jp_summary_invoice_carryover/__manifest__.py new file mode 100644 index 00000000..be04a0fb --- /dev/null +++ b/l10n_jp_summary_invoice_carryover/__manifest__.py @@ -0,0 +1,21 @@ +# Copyright 2025 Quartile (https://www.quartile.co) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +{ + "name": "Japan Summary Invoice - Carryover", + "summary": "Add carryover amount tracking to summary invoices", + "version": "19.0.1.0.0", + "category": "Japanese Localization", + "author": "Quartile, Odoo Community Association (OCA)", + "website": "https://github.com/OCA/l10n-japan", + "license": "AGPL-3", + "depends": ["l10n_jp_summary_invoice"], + "data": [ + "reports/report_summary_invoice_templates.xml", + "views/account_billing_views.xml", + "views/res_config_settings_views.xml", + "views/res_partner_views.xml", + ], + "development_status": "Alpha", + "maintainers": ["yostashiro", "aungkokolin1997"], + "installable": True, +} diff --git a/l10n_jp_summary_invoice_carryover/i18n/ja.po b/l10n_jp_summary_invoice_carryover/i18n/ja.po new file mode 100644 index 00000000..bc86f00a --- /dev/null +++ b/l10n_jp_summary_invoice_carryover/i18n/ja.po @@ -0,0 +1,222 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * l10n_jp_summary_invoice_carryover +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 18.0+e\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2025-12-30 16:40+0000\n" +"PO-Revision-Date: 2025-12-30 16:40+0000\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: l10n_jp_summary_invoice_carryover +#: model_terms:ir.ui.view,arch_db:l10n_jp_summary_invoice_carryover.view_account_billing_form_carryover +msgid "Manual Adj." +msgstr "手入力" + +#. module: l10n_jp_summary_invoice_carryover +#: model_terms:ir.ui.view,arch_db:l10n_jp_summary_invoice_carryover.report_summary_invoice_document_carryover +msgid "Total Billed Amt" +msgstr "今回請求額" + +#. module: l10n_jp_summary_invoice_carryover +#: model:ir.model,name:l10n_jp_summary_invoice_carryover.model_account_billing +msgid "Account Billing" +msgstr "合計請求書" + +#. module: l10n_jp_summary_invoice_carryover +#: model_terms:ir.ui.view,arch_db:l10n_jp_summary_invoice_carryover.view_account_billing_form_carryover +msgid "Carryover" +msgstr "繰越" + +#. module: l10n_jp_summary_invoice_carryover +#: model:ir.model.fields,field_description:l10n_jp_summary_invoice_carryover.field_account_billing__carryover_amount +msgid "Carryover Amount" +msgstr "繰越額" + +#. module: l10n_jp_summary_invoice_carryover +#: model_terms:ir.ui.view,arch_db:l10n_jp_summary_invoice_carryover.report_summary_invoice_document_carryover +msgid "Carryover Amt" +msgstr "繰越額" + +#. module: l10n_jp_summary_invoice_carryover +#: model:ir.model.fields,help:l10n_jp_summary_invoice_carryover.field_account_billing__total_billed_amount +msgid "Carryover plus current billed amount." +msgstr "繰越額と今回取引額の合計。" + +#. module: l10n_jp_summary_invoice_carryover +#: model:ir.model,name:l10n_jp_summary_invoice_carryover.model_res_company +msgid "Companies" +msgstr "会社" + +#. module: l10n_jp_summary_invoice_carryover +#: model:ir.model,name:l10n_jp_summary_invoice_carryover.model_res_config_settings +msgid "Config Settings" +msgstr "コンフィグ設定" + +#. module: l10n_jp_summary_invoice_carryover +#: model:ir.model,name:l10n_jp_summary_invoice_carryover.model_res_partner +msgid "Contact" +msgstr "連絡先" + +#. module: l10n_jp_summary_invoice_carryover +#: model_terms:ir.ui.view,arch_db:l10n_jp_summary_invoice_carryover.view_account_billing_form_carryover +msgid "Current Purchase Amount" +msgstr "今回取引額" + +#. module: l10n_jp_summary_invoice_carryover +#: model_terms:ir.ui.view,arch_db:l10n_jp_summary_invoice_carryover.report_summary_invoice_document_carryover +msgid "Current Purchase Amt" +msgstr "今回取引額" + +#. module: l10n_jp_summary_invoice_carryover +#: model:ir.model.fields,help:l10n_jp_summary_invoice_carryover.field_res_company__show_carryover_amounts +#: model:ir.model.fields,help:l10n_jp_summary_invoice_carryover.field_res_config_settings__show_carryover_amounts +msgid "" +"If enabled, carryover amount fields will be displayed in the summary invoice " +"report." +msgstr "有効にすると、繰越額情報が合計請求書レポートに表示されます。" + +#. module: l10n_jp_summary_invoice_carryover +#: model:ir.model.fields,help:l10n_jp_summary_invoice_carryover.field_account_billing__payment_amount_manual +msgid "Manual override for payment amount." +msgstr "入金額の手入力値。" + +#. module: l10n_jp_summary_invoice_carryover +#: model:ir.model.fields,help:l10n_jp_summary_invoice_carryover.field_account_billing__prev_billed_amount_manual +msgid "Manual override for previous billed amount." +msgstr "前回請求額の手入力値。" + +#. module: l10n_jp_summary_invoice_carryover +#: model:ir.model.fields.selection,name:l10n_jp_summary_invoice_carryover.selection__res_partner__show_carryover_amounts__no +msgid "No" +msgstr "いいえ" + +#. module: l10n_jp_summary_invoice_carryover +#: model:ir.model.fields,field_description:l10n_jp_summary_invoice_carryover.field_account_billing__payment_amount +#: model_terms:ir.ui.view,arch_db:l10n_jp_summary_invoice_carryover.view_account_billing_form_carryover +msgid "Payment Amount" +msgstr "入金額" + +#. module: l10n_jp_summary_invoice_carryover +#: model:ir.model.fields,field_description:l10n_jp_summary_invoice_carryover.field_account_billing__payment_amount_manual +msgid "Payment Amount (Manual)" +msgstr "入金額(手入力)" + +#. module: l10n_jp_summary_invoice_carryover +#: model_terms:ir.ui.view,arch_db:l10n_jp_summary_invoice_carryover.report_summary_invoice_document_carryover +msgid "Payment Amt" +msgstr "入金額" + +#. module: l10n_jp_summary_invoice_carryover +#: model:ir.model.fields,help:l10n_jp_summary_invoice_carryover.field_account_billing__payment_amount +msgid "Payments received on previous billing invoices." +msgstr "前回請求書に対する入金額。" + +#. module: l10n_jp_summary_invoice_carryover +#: model:ir.model.fields,field_description:l10n_jp_summary_invoice_carryover.field_account_billing__prev_billed_amount +#: model_terms:ir.ui.view,arch_db:l10n_jp_summary_invoice_carryover.view_account_billing_form_carryover +msgid "Previous Billed Amount" +msgstr "前回請求額" + +#. module: l10n_jp_summary_invoice_carryover +#: model:ir.model.fields,field_description:l10n_jp_summary_invoice_carryover.field_account_billing__prev_billed_amount_manual +msgid "Previous Billed Amount (Manual)" +msgstr "前回請求額(手入力)" + +#. module: l10n_jp_summary_invoice_carryover +#: model_terms:ir.ui.view,arch_db:l10n_jp_summary_invoice_carryover.report_summary_invoice_document_carryover +msgid "Previous Billed Amt" +msgstr "前回請求額" + +#. module: l10n_jp_summary_invoice_carryover +#: model:ir.model.fields,field_description:l10n_jp_summary_invoice_carryover.field_account_billing__prev_billing_id +msgid "Previous Billing" +msgstr "前回合計請求" + +#. module: l10n_jp_summary_invoice_carryover +#: model:ir.model.fields,field_description:l10n_jp_summary_invoice_carryover.field_account_billing__prev_billing_candidate_ids +msgid "Previous Billing Candidates" +msgstr "前回合計請求候補" + +#. module: l10n_jp_summary_invoice_carryover +#: model:ir.model.fields,help:l10n_jp_summary_invoice_carryover.field_account_billing__carryover_amount +msgid "Previous billing amount minus payments." +msgstr "前回請求額から入金額を差し引いた金額。" + +#. module: l10n_jp_summary_invoice_carryover +#: model:ir.model.fields,field_description:l10n_jp_summary_invoice_carryover.field_account_billing__show_carryover_amounts +#: model:ir.model.fields,field_description:l10n_jp_summary_invoice_carryover.field_res_company__show_carryover_amounts +#: model:ir.model.fields,field_description:l10n_jp_summary_invoice_carryover.field_res_config_settings__show_carryover_amounts +#: model:ir.model.fields,field_description:l10n_jp_summary_invoice_carryover.field_res_partner__show_carryover_amounts +#: model:ir.model.fields,field_description:l10n_jp_summary_invoice_carryover.field_res_users__show_carryover_amounts +msgid "Show Carryover Amounts" +msgstr "繰越額を表示" + +#. module: l10n_jp_summary_invoice_carryover +#: model_terms:ir.ui.view,arch_db:l10n_jp_summary_invoice_carryover.view_partner_form_carryover +msgid "Summary Invoice" +msgstr "合計請求書" + +#. module: l10n_jp_summary_invoice_carryover +#: model_terms:ir.ui.view,arch_db:l10n_jp_summary_invoice_carryover.res_config_settings_view_form +msgid "" +"The carryover amounts will appear in the summary invoice report if selected" +msgstr "選択すると、繰越額が合計請求書レポートに表示されます" + +#. module: l10n_jp_summary_invoice_carryover +#: model:ir.model.fields,field_description:l10n_jp_summary_invoice_carryover.field_account_billing__total_billed_amount +msgid "Total Billed Amount" +msgstr "今回請求額" + +#. module: l10n_jp_summary_invoice_carryover +#: model_terms:ir.ui.view,arch_db:l10n_jp_summary_invoice_carryover.report_summary_invoice_document_carryover +msgid "Total Billed Amt" +msgstr "今回請求額" + +#. module: l10n_jp_summary_invoice_carryover +#: model:ir.model.fields,help:l10n_jp_summary_invoice_carryover.field_account_billing__prev_billed_amount +msgid "Total billed amount from the previous billing." +msgstr "前回合計請求書の請求額。" + +#. module: l10n_jp_summary_invoice_carryover +#: model:ir.model.fields.selection,name:l10n_jp_summary_invoice_carryover.selection__res_partner__show_carryover_amounts__default +msgid "Use Company Default" +msgstr "会社のデフォルトを使用" + +#. module: l10n_jp_summary_invoice_carryover +#: model:ir.model.fields,field_description:l10n_jp_summary_invoice_carryover.field_account_billing__use_payment_amount_manual +msgid "Use Manual Payment Amount" +msgstr "入金額を手入力" + +#. module: l10n_jp_summary_invoice_carryover +#: model:ir.model.fields,field_description:l10n_jp_summary_invoice_carryover.field_account_billing__use_prev_billed_amount_manual +msgid "Use Manual Previous Billed Amount" +msgstr "前回請求額を手入力" + +#. module: l10n_jp_summary_invoice_carryover +#: model:ir.model.fields,help:l10n_jp_summary_invoice_carryover.field_account_billing__show_carryover_amounts +msgid "Whether to show carryover amounts in the summary invoice report." +msgstr "合計請求書レポートに繰越額を表示するかどうか。" + +#. module: l10n_jp_summary_invoice_carryover +#: model:ir.model.fields,help:l10n_jp_summary_invoice_carryover.field_res_partner__show_carryover_amounts +#: model:ir.model.fields,help:l10n_jp_summary_invoice_carryover.field_res_users__show_carryover_amounts +msgid "" +"Whether to show carryover amounts in the summary invoice report. If set to " +"'Use Company Default', the company setting will be used." +msgstr "" +"合計請求書レポートに繰越額を表示するかどうか。「会社のデフォルトを使用」の場" +"合は、会社の設定が使用されます。" + +#. module: l10n_jp_summary_invoice_carryover +#: model:ir.model.fields.selection,name:l10n_jp_summary_invoice_carryover.selection__res_partner__show_carryover_amounts__yes +msgid "Yes" +msgstr "はい" diff --git a/l10n_jp_summary_invoice_carryover/i18n/l10n_jp_summary_invoice_carryover.pot b/l10n_jp_summary_invoice_carryover/i18n/l10n_jp_summary_invoice_carryover.pot new file mode 100644 index 00000000..ef7fbeee --- /dev/null +++ b/l10n_jp_summary_invoice_carryover/i18n/l10n_jp_summary_invoice_carryover.pot @@ -0,0 +1,217 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * l10n_jp_summary_invoice_carryover +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 18.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: l10n_jp_summary_invoice_carryover +#: model_terms:ir.ui.view,arch_db:l10n_jp_summary_invoice_carryover.view_account_billing_form_carryover +msgid "Manual Adj." +msgstr "" + +#. module: l10n_jp_summary_invoice_carryover +#: model_terms:ir.ui.view,arch_db:l10n_jp_summary_invoice_carryover.report_summary_invoice_document_carryover +msgid "Total Billed Amt" +msgstr "" + +#. module: l10n_jp_summary_invoice_carryover +#: model:ir.model,name:l10n_jp_summary_invoice_carryover.model_account_billing +msgid "Account Billing" +msgstr "" + +#. module: l10n_jp_summary_invoice_carryover +#: model_terms:ir.ui.view,arch_db:l10n_jp_summary_invoice_carryover.view_account_billing_form_carryover +msgid "Carryover" +msgstr "" + +#. module: l10n_jp_summary_invoice_carryover +#: model:ir.model.fields,field_description:l10n_jp_summary_invoice_carryover.field_account_billing__carryover_amount +msgid "Carryover Amount" +msgstr "" + +#. module: l10n_jp_summary_invoice_carryover +#: model_terms:ir.ui.view,arch_db:l10n_jp_summary_invoice_carryover.report_summary_invoice_document_carryover +msgid "Carryover Amt" +msgstr "" + +#. module: l10n_jp_summary_invoice_carryover +#: model:ir.model.fields,help:l10n_jp_summary_invoice_carryover.field_account_billing__total_billed_amount +msgid "Carryover plus current billed amount." +msgstr "" + +#. module: l10n_jp_summary_invoice_carryover +#: model:ir.model,name:l10n_jp_summary_invoice_carryover.model_res_company +msgid "Companies" +msgstr "" + +#. module: l10n_jp_summary_invoice_carryover +#: model:ir.model,name:l10n_jp_summary_invoice_carryover.model_res_config_settings +msgid "Config Settings" +msgstr "" + +#. module: l10n_jp_summary_invoice_carryover +#: model:ir.model,name:l10n_jp_summary_invoice_carryover.model_res_partner +msgid "Contact" +msgstr "" + +#. module: l10n_jp_summary_invoice_carryover +#: model_terms:ir.ui.view,arch_db:l10n_jp_summary_invoice_carryover.view_account_billing_form_carryover +msgid "Current Purchase Amount" +msgstr "" + +#. module: l10n_jp_summary_invoice_carryover +#: model_terms:ir.ui.view,arch_db:l10n_jp_summary_invoice_carryover.report_summary_invoice_document_carryover +msgid "Current Purchase Amt" +msgstr "" + +#. module: l10n_jp_summary_invoice_carryover +#: model:ir.model.fields,help:l10n_jp_summary_invoice_carryover.field_res_company__show_carryover_amounts +#: model:ir.model.fields,help:l10n_jp_summary_invoice_carryover.field_res_config_settings__show_carryover_amounts +msgid "" +"If enabled, carryover amount fields will be displayed in the summary invoice" +" report." +msgstr "" + +#. module: l10n_jp_summary_invoice_carryover +#: model:ir.model.fields,help:l10n_jp_summary_invoice_carryover.field_account_billing__payment_amount_manual +msgid "Manual override for payment amount." +msgstr "" + +#. module: l10n_jp_summary_invoice_carryover +#: model:ir.model.fields,help:l10n_jp_summary_invoice_carryover.field_account_billing__prev_billed_amount_manual +msgid "Manual override for previous billed amount." +msgstr "" + +#. module: l10n_jp_summary_invoice_carryover +#: model:ir.model.fields.selection,name:l10n_jp_summary_invoice_carryover.selection__res_partner__show_carryover_amounts__no +msgid "No" +msgstr "" + +#. module: l10n_jp_summary_invoice_carryover +#: model:ir.model.fields,field_description:l10n_jp_summary_invoice_carryover.field_account_billing__payment_amount +#: model_terms:ir.ui.view,arch_db:l10n_jp_summary_invoice_carryover.view_account_billing_form_carryover +msgid "Payment Amount" +msgstr "" + +#. module: l10n_jp_summary_invoice_carryover +#: model:ir.model.fields,field_description:l10n_jp_summary_invoice_carryover.field_account_billing__payment_amount_manual +msgid "Payment Amount (Manual)" +msgstr "" + +#. module: l10n_jp_summary_invoice_carryover +#: model_terms:ir.ui.view,arch_db:l10n_jp_summary_invoice_carryover.report_summary_invoice_document_carryover +msgid "Payment Amt" +msgstr "" + +#. module: l10n_jp_summary_invoice_carryover +#: model:ir.model.fields,help:l10n_jp_summary_invoice_carryover.field_account_billing__payment_amount +msgid "Payments received on previous billing invoices." +msgstr "" + +#. module: l10n_jp_summary_invoice_carryover +#: model:ir.model.fields,field_description:l10n_jp_summary_invoice_carryover.field_account_billing__prev_billed_amount +#: model_terms:ir.ui.view,arch_db:l10n_jp_summary_invoice_carryover.view_account_billing_form_carryover +msgid "Previous Billed Amount" +msgstr "" + +#. module: l10n_jp_summary_invoice_carryover +#: model:ir.model.fields,field_description:l10n_jp_summary_invoice_carryover.field_account_billing__prev_billed_amount_manual +msgid "Previous Billed Amount (Manual)" +msgstr "" + +#. module: l10n_jp_summary_invoice_carryover +#: model_terms:ir.ui.view,arch_db:l10n_jp_summary_invoice_carryover.report_summary_invoice_document_carryover +msgid "Previous Billed Amt" +msgstr "" + +#. module: l10n_jp_summary_invoice_carryover +#: model:ir.model.fields,field_description:l10n_jp_summary_invoice_carryover.field_account_billing__prev_billing_id +msgid "Previous Billing" +msgstr "" + +#. module: l10n_jp_summary_invoice_carryover +#: model:ir.model.fields,field_description:l10n_jp_summary_invoice_carryover.field_account_billing__prev_billing_candidate_ids +msgid "Previous Billing Candidates" +msgstr "" + +#. module: l10n_jp_summary_invoice_carryover +#: model:ir.model.fields,help:l10n_jp_summary_invoice_carryover.field_account_billing__carryover_amount +msgid "Previous billing amount minus payments." +msgstr "" + +#. module: l10n_jp_summary_invoice_carryover +#: model:ir.model.fields,field_description:l10n_jp_summary_invoice_carryover.field_account_billing__show_carryover_amounts +#: model:ir.model.fields,field_description:l10n_jp_summary_invoice_carryover.field_res_company__show_carryover_amounts +#: model:ir.model.fields,field_description:l10n_jp_summary_invoice_carryover.field_res_config_settings__show_carryover_amounts +#: model:ir.model.fields,field_description:l10n_jp_summary_invoice_carryover.field_res_partner__show_carryover_amounts +#: model:ir.model.fields,field_description:l10n_jp_summary_invoice_carryover.field_res_users__show_carryover_amounts +msgid "Show Carryover Amounts" +msgstr "" + +#. module: l10n_jp_summary_invoice_carryover +#: model_terms:ir.ui.view,arch_db:l10n_jp_summary_invoice_carryover.view_partner_form_carryover +msgid "Summary Invoice" +msgstr "" + +#. module: l10n_jp_summary_invoice_carryover +#: model_terms:ir.ui.view,arch_db:l10n_jp_summary_invoice_carryover.res_config_settings_view_form +msgid "" +"The carryover amounts will appear in the summary invoice report if selected" +msgstr "" + +#. module: l10n_jp_summary_invoice_carryover +#: model:ir.model.fields,field_description:l10n_jp_summary_invoice_carryover.field_account_billing__total_billed_amount +msgid "Total Billed Amount" +msgstr "" + +#. module: l10n_jp_summary_invoice_carryover +#: model_terms:ir.ui.view,arch_db:l10n_jp_summary_invoice_carryover.report_summary_invoice_document_carryover +msgid "Total Billed Amt" +msgstr "" + +#. module: l10n_jp_summary_invoice_carryover +#: model:ir.model.fields,help:l10n_jp_summary_invoice_carryover.field_account_billing__prev_billed_amount +msgid "Total billed amount from the previous billing." +msgstr "" + +#. module: l10n_jp_summary_invoice_carryover +#: model:ir.model.fields.selection,name:l10n_jp_summary_invoice_carryover.selection__res_partner__show_carryover_amounts__default +msgid "Use Company Default" +msgstr "" + +#. module: l10n_jp_summary_invoice_carryover +#: model:ir.model.fields,field_description:l10n_jp_summary_invoice_carryover.field_account_billing__use_payment_amount_manual +msgid "Use Manual Payment Amount" +msgstr "" + +#. module: l10n_jp_summary_invoice_carryover +#: model:ir.model.fields,field_description:l10n_jp_summary_invoice_carryover.field_account_billing__use_prev_billed_amount_manual +msgid "Use Manual Previous Billed Amount" +msgstr "" + +#. module: l10n_jp_summary_invoice_carryover +#: model:ir.model.fields,help:l10n_jp_summary_invoice_carryover.field_account_billing__show_carryover_amounts +msgid "Whether to show carryover amounts in the summary invoice report." +msgstr "" + +#. module: l10n_jp_summary_invoice_carryover +#: model:ir.model.fields,help:l10n_jp_summary_invoice_carryover.field_res_partner__show_carryover_amounts +#: model:ir.model.fields,help:l10n_jp_summary_invoice_carryover.field_res_users__show_carryover_amounts +msgid "" +"Whether to show carryover amounts in the summary invoice report. If set to " +"'Use Company Default', the company setting will be used." +msgstr "" + +#. module: l10n_jp_summary_invoice_carryover +#: model:ir.model.fields.selection,name:l10n_jp_summary_invoice_carryover.selection__res_partner__show_carryover_amounts__yes +msgid "Yes" +msgstr "" diff --git a/l10n_jp_summary_invoice_carryover/models/__init__.py b/l10n_jp_summary_invoice_carryover/models/__init__.py new file mode 100644 index 00000000..63cd2f38 --- /dev/null +++ b/l10n_jp_summary_invoice_carryover/models/__init__.py @@ -0,0 +1,4 @@ +from . import account_billing +from . import res_company +from . import res_config_settings +from . import res_partner diff --git a/l10n_jp_summary_invoice_carryover/models/account_billing.py b/l10n_jp_summary_invoice_carryover/models/account_billing.py new file mode 100644 index 00000000..77bf3a7d --- /dev/null +++ b/l10n_jp_summary_invoice_carryover/models/account_billing.py @@ -0,0 +1,183 @@ +# Copyright 2025 Quartile (https://www.quartile.co) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models + + +class AccountBilling(models.Model): + _inherit = "account.billing" + + prev_billing_id = fields.Many2one( + "account.billing", + string="Previous Billing", + compute="_compute_prev_billing_id", + store=True, + readonly=False, + domain="[('id', 'in', prev_billing_candidate_ids)]", + tracking=True, + ) + prev_billing_candidate_ids = fields.Many2many( + "account.billing", + string="Previous Billing Candidates", + compute="_compute_prev_billing_candidate_ids", + ) + use_prev_billed_amount_manual = fields.Boolean( + string="Use Manual Previous Billed Amount", + tracking=True, + copy=False, + ) + prev_billed_amount_manual = fields.Monetary( + string="Previous Billed Amount (Manual)", + tracking=True, + copy=False, + help="Manual override for previous billed amount.", + ) + prev_billed_amount = fields.Monetary( + string="Previous Billed Amount", + compute="_compute_carryover_amounts", + store=True, + help="Total billed amount from the previous billing.", + ) + use_payment_amount_manual = fields.Boolean( + string="Use Manual Payment Amount", + tracking=True, + copy=False, + ) + payment_amount_manual = fields.Monetary( + string="Payment Amount (Manual)", + tracking=True, + copy=False, + help="Manual override for payment amount.", + ) + payment_amount = fields.Monetary( + compute="_compute_carryover_amounts", + store=True, + help="Payments received on previous billing invoices.", + ) + carryover_amount = fields.Monetary( + compute="_compute_carryover_amounts", + store=True, + help="Previous billing amount minus payments.", + ) + total_billed_amount = fields.Monetary( + compute="_compute_carryover_amounts", + store=True, + recursive=True, + help="Carryover plus current billed amount.", + ) + show_carryover_amounts = fields.Boolean( + compute="_compute_show_carryover_amounts", + store=True, + readonly=False, + help="Whether to show carryover amounts in the summary invoice report.", + ) + + def _get_prev_billing_domain(self): + self.ensure_one() + return [ + ("company_id", "=", self.company_id.id), + ( + "partner_id.commercial_partner_id", + "=", + self.partner_id.commercial_partner_id.id, + ), + ("bill_type", "=", self.bill_type), + ("currency_id", "=", self.currency_id.id), + ("state", "=", "billed"), + ("date", "<", self.date or fields.Date.today()), + ("id", "!=", self.id), + ] + + @api.depends( + "partner_id", "bill_type", "currency_id", "date", "state", "company_id" + ) + def _compute_prev_billing_candidate_ids(self): + for rec in self: + rec.prev_billing_candidate_ids = self.search(rec._get_prev_billing_domain()) + + @api.depends( + "partner_id", "bill_type", "currency_id", "date", "state", "company_id" + ) + def _compute_prev_billing_id(self): + for rec in self: + rec.prev_billing_id = self.search( + rec._get_prev_billing_domain(), + order="date desc, id desc", + limit=1, + ) + + def _get_prev_billed_amount(self): + self.ensure_one() + return ( + self.prev_billed_amount_manual + if self.use_prev_billed_amount_manual + else self.prev_billed_amount + ) + + def _get_payment_amount(self): + self.ensure_one() + return ( + self.payment_amount_manual + if self.use_payment_amount_manual + else self.payment_amount + ) + + @api.depends( + "prev_billing_id", + "use_prev_billed_amount_manual", + "prev_billed_amount_manual", + "use_payment_amount_manual", + "payment_amount_manual", + "amount_total", + "prev_billing_id.total_billed_amount", + "prev_billing_id.billing_line_ids.amount_residual", + "prev_billing_id.tax_adjustment_entry_id.amount_residual", + ) + def _compute_carryover_amounts(self): + for rec in self: + rec.prev_billed_amount = 0.0 + rec.payment_amount = 0.0 + rec.carryover_amount = 0.0 + rec.total_billed_amount = rec.amount_total + prev_billing = rec.prev_billing_id + if prev_billing: + rec.prev_billed_amount = prev_billing.total_billed_amount + # The amount still outstanding is the residual of every previous + # billing in the chain, not just the immediately previous one: each + # invoice belongs to a single billing, so the unpaid amount carried + # over from older periods only lives on those earlier billings. + prior_billings = rec.prev_billing_candidate_ids + outstanding = sum( + prior_billings.billing_line_ids.mapped("amount_residual") + ) + sum( + prior_billings.mapped("tax_adjustment_entry_id.amount_residual") + ) + rec.payment_amount = rec.prev_billed_amount - outstanding + prev_billed_amount = rec._get_prev_billed_amount() + payment_amount = rec._get_payment_amount() + rec.carryover_amount = prev_billed_amount - payment_amount + rec.total_billed_amount = rec.carryover_amount + rec.amount_total + + @api.depends( + "partner_id.commercial_partner_id.show_carryover_amounts", + "company_id.show_carryover_amounts", + ) + def _compute_show_carryover_amounts(self): + for rec in self: + partner = rec.partner_id.commercial_partner_id + if partner.show_carryover_amounts == "yes": + rec.show_carryover_amounts = True + elif partner.show_carryover_amounts == "no": + rec.show_carryover_amounts = False + else: + rec.show_carryover_amounts = rec.company_id.show_carryover_amounts + + def validate_billing(self): + for rec in self: + if not rec.use_prev_billed_amount_manual: + rec.use_prev_billed_amount_manual = True + rec.prev_billed_amount_manual = rec.prev_billed_amount + if not rec.use_payment_amount_manual: + rec.use_payment_amount_manual = True + rec.payment_amount_manual = rec.payment_amount + return super().validate_billing() diff --git a/l10n_jp_summary_invoice_carryover/models/res_company.py b/l10n_jp_summary_invoice_carryover/models/res_company.py new file mode 100644 index 00000000..64b568e9 --- /dev/null +++ b/l10n_jp_summary_invoice_carryover/models/res_company.py @@ -0,0 +1,14 @@ +# Copyright 2025 Quartile (https://www.quartile.co) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class ResCompany(models.Model): + _inherit = "res.company" + + show_carryover_amounts = fields.Boolean( + default=True, + help="If enabled, carryover amount fields will be displayed in the summary " + "invoice report.", + ) diff --git a/l10n_jp_summary_invoice_carryover/models/res_config_settings.py b/l10n_jp_summary_invoice_carryover/models/res_config_settings.py new file mode 100644 index 00000000..709b6ed6 --- /dev/null +++ b/l10n_jp_summary_invoice_carryover/models/res_config_settings.py @@ -0,0 +1,12 @@ +# Copyright 2025 Quartile (https://www.quartile.co) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class ResConfigSettings(models.TransientModel): + _inherit = "res.config.settings" + + show_carryover_amounts = fields.Boolean( + related="company_id.show_carryover_amounts", readonly=False + ) diff --git a/l10n_jp_summary_invoice_carryover/models/res_partner.py b/l10n_jp_summary_invoice_carryover/models/res_partner.py new file mode 100644 index 00000000..4ffd6ff8 --- /dev/null +++ b/l10n_jp_summary_invoice_carryover/models/res_partner.py @@ -0,0 +1,19 @@ +# Copyright 2025 Quartile (https://www.quartile.co) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class ResPartner(models.Model): + _inherit = "res.partner" + + show_carryover_amounts = fields.Selection( + selection=[ + ("default", "Use Company Default"), + ("yes", "Yes"), + ("no", "No"), + ], + default="default", + help="Whether to show carryover amounts in the summary invoice report. " + "If set to 'Use Company Default', the company setting will be used.", + ) diff --git a/l10n_jp_summary_invoice_carryover/pyproject.toml b/l10n_jp_summary_invoice_carryover/pyproject.toml new file mode 100644 index 00000000..4231d0cc --- /dev/null +++ b/l10n_jp_summary_invoice_carryover/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/l10n_jp_summary_invoice_carryover/readme/CONFIGURE.md b/l10n_jp_summary_invoice_carryover/readme/CONFIGURE.md new file mode 100644 index 00000000..b55c384e --- /dev/null +++ b/l10n_jp_summary_invoice_carryover/readme/CONFIGURE.md @@ -0,0 +1,16 @@ +The visibility of carryover amounts in the report can be controlled at three +levels: + +1. **Company** (default for all partners): Go to *Settings > Invoicing > + Japan Summary Invoice* and enable/disable **Show Carryover Amounts**. + +2. **Partner** (override for specific customers): Go to the partner form, + under *Sales & Purchases > Summary Invoice*, set **Show Carryover Amounts** + to: + - *Use Company Default*: Follow the company setting + - *Yes*: Always show carryover amounts + - *No*: Never show carryover amounts + +3. **Billing** (per-billing override): In the billing form under the + *Carryover* tab, the **Show Carryover Amounts** checkbox is computed + from the partner setting but can be manually adjusted. diff --git a/l10n_jp_summary_invoice_carryover/readme/CONTRIBUTORS.md b/l10n_jp_summary_invoice_carryover/readme/CONTRIBUTORS.md new file mode 100644 index 00000000..4d16bdc2 --- /dev/null +++ b/l10n_jp_summary_invoice_carryover/readme/CONTRIBUTORS.md @@ -0,0 +1,3 @@ +- [Quartile](https://www.quartile.co): + - Yoshi Tashiro + - Aung Ko Ko Lin diff --git a/l10n_jp_summary_invoice_carryover/readme/DESCRIPTION.md b/l10n_jp_summary_invoice_carryover/readme/DESCRIPTION.md new file mode 100644 index 00000000..ea1ed009 --- /dev/null +++ b/l10n_jp_summary_invoice_carryover/readme/DESCRIPTION.md @@ -0,0 +1,14 @@ +This module extends Japan Summary Invoice to support carryover amount tracking +for recurring billing cycles. + +It adds the following fields to billings: + +- **Previous Billed Amount**: Total from the previous billing period +- **Payment Amount**: Payments received against the previous billing +- **Carryover Amount**: Outstanding balance carried forward +- **Total Billed Amount**: Carryover plus current purchases + +Manual override fields are available to adjust values when needed. + +The visibility of carryover amounts in the report can be controlled at +company, partner, and billing levels. diff --git a/l10n_jp_summary_invoice_carryover/readme/USAGE.md b/l10n_jp_summary_invoice_carryover/readme/USAGE.md new file mode 100644 index 00000000..9796e6c0 --- /dev/null +++ b/l10n_jp_summary_invoice_carryover/readme/USAGE.md @@ -0,0 +1,12 @@ +1. When creating a new billing, the system automatically finds the most + recent validated billing for the same partner and calculates: + - **Previous Billed Amount**: Total amount from the previous billing. + - **Payment Amount**: Payments received on the previous billing invoices. + - **Carryover Amount**: Previous billed amount minus payments. +2. Use the **Manual Adj.** toggle to manually override the computed values + if needed. This is useful when the previous billing was created before + this module was installed or when adjustments are required. +3. When the billing is validated, the current computed values are + automatically frozen by enabling the manual override toggles. This + ensures that subsequent payments on previous invoices do not affect + the validated billing's carryover amounts. diff --git a/l10n_jp_summary_invoice_carryover/reports/report_summary_invoice_templates.xml b/l10n_jp_summary_invoice_carryover/reports/report_summary_invoice_templates.xml new file mode 100644 index 00000000..011f37c6 --- /dev/null +++ b/l10n_jp_summary_invoice_carryover/reports/report_summary_invoice_templates.xml @@ -0,0 +1,89 @@ + + + diff --git a/l10n_jp_summary_invoice_carryover/static/description/icon.png b/l10n_jp_summary_invoice_carryover/static/description/icon.png new file mode 100644 index 00000000..1dcc49c2 Binary files /dev/null and b/l10n_jp_summary_invoice_carryover/static/description/icon.png differ diff --git a/l10n_jp_summary_invoice_carryover/static/description/index.html b/l10n_jp_summary_invoice_carryover/static/description/index.html new file mode 100644 index 00000000..6a759d1e --- /dev/null +++ b/l10n_jp_summary_invoice_carryover/static/description/index.html @@ -0,0 +1,496 @@ + + + + + +README.rst + + + +
+ + + +Odoo Community Association + +
+

Japan Summary Invoice - Carryover

+ +

Alpha License: AGPL-3 OCA/l10n-japan Translate me on Weblate Try me on Runboat

+

This module extends Japan Summary Invoice to support carryover amount +tracking for recurring billing cycles.

+

It adds the following fields to billings:

+
    +
  • Previous Billed Amount: Total from the previous billing period
  • +
  • Payment Amount: Payments received against the previous billing
  • +
  • Carryover Amount: Outstanding balance carried forward
  • +
  • Total Billed Amount: Carryover plus current purchases
  • +
+

Manual override fields are available to adjust values when needed.

+

The visibility of carryover amounts in the report can be controlled at +company, partner, and billing levels.

+
+

Important

+

This is an alpha version, the data model and design can change at any time without warning. +Only for development or testing purpose, do not use in production. +More details on development status

+
+

Table of contents

+ +
+

Configuration

+

The visibility of carryover amounts in the report can be controlled at +three levels:

+
    +
  1. Company (default for all partners): Go to Settings > Invoicing > +Japan Summary Invoice and enable/disable Show Carryover Amounts.
  2. +
  3. Partner (override for specific customers): Go to the partner +form, under Sales & Purchases > Summary Invoice, set Show +Carryover Amounts to:
      +
    • Use Company Default: Follow the company setting
    • +
    • Yes: Always show carryover amounts
    • +
    • No: Never show carryover amounts
    • +
    +
  4. +
  5. Billing (per-billing override): In the billing form under the +Carryover tab, the Show Carryover Amounts checkbox is computed +from the partner setting but can be manually adjusted.
  6. +
+
+
+

Usage

+
    +
  1. When creating a new billing, the system automatically finds the most +recent validated billing for the same partner and calculates:
      +
    • Previous Billed Amount: Total amount from the previous +billing.
    • +
    • Payment Amount: Payments received on the previous billing +invoices.
    • +
    • Carryover Amount: Previous billed amount minus payments.
    • +
    +
  2. +
  3. Use the Manual Adj. toggle to manually override the computed +values if needed. This is useful when the previous billing was +created before this module was installed or when adjustments are +required.
  4. +
  5. When the billing is validated, the current computed values are +automatically frozen by enabling the manual override toggles. This +ensures that subsequent payments on previous invoices do not affect +the validated billing’s carryover amounts.
  6. +
+
+
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • Quartile
  • +
+
+
+

Contributors

+
    +
  • Quartile:
      +
    • Yoshi Tashiro
    • +
    • Aung Ko Ko Lin
    • +
    +
  • +
+
+
+

Maintainers

+

This module is maintained by the OCA.

+ +Odoo Community Association + +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

Current maintainers:

+

yostashiro aungkokolin1997

+

This module is part of the OCA/l10n-japan project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+
+ + diff --git a/l10n_jp_summary_invoice_carryover/tests/__init__.py b/l10n_jp_summary_invoice_carryover/tests/__init__.py new file mode 100644 index 00000000..bba38fd1 --- /dev/null +++ b/l10n_jp_summary_invoice_carryover/tests/__init__.py @@ -0,0 +1 @@ +from . import test_l10n_jp_summary_invoice_carryover diff --git a/l10n_jp_summary_invoice_carryover/tests/test_l10n_jp_summary_invoice_carryover.py b/l10n_jp_summary_invoice_carryover/tests/test_l10n_jp_summary_invoice_carryover.py new file mode 100644 index 00000000..358ec27c --- /dev/null +++ b/l10n_jp_summary_invoice_carryover/tests/test_l10n_jp_summary_invoice_carryover.py @@ -0,0 +1,299 @@ +# Copyright 2025 Quartile (https://www.quartile.co) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from datetime import date + +from odoo.fields import Command +from odoo.tests.common import TransactionCase + + +class TestSummaryInvoiceCarryover(TransactionCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.company = cls.env["res.company"].create( + { + "name": "test company", + "currency_id": cls.env.ref("base.JPY").id, + "country_id": cls.env.ref("base.jp").id, + "tax_calculation_rounding_method": "round_globally", + } + ) + cls.env = cls.env( + context={"allowed_company_ids": [cls.company.id], "tracking_disable": True} + ) + account_receivable = cls.env["account.account"].create( + {"code": "recv", "name": "Receivable", "account_type": "asset_receivable"} + ) + cls.account_income = cls.env["account.account"].create( + {"code": "income", "name": "Income", "account_type": "income"} + ) + account_bank = cls.env["account.account"].create( + {"code": "bank", "name": "Bank", "account_type": "asset_cash"} + ) + cls.partner = cls.env["res.partner"].create( + { + "name": "Test Partner", + "property_account_receivable_id": account_receivable.id, + } + ) + tax_group = cls.env["account.tax.group"].create({"name": "Tax Group"}) + cls.tax_10 = cls.env["account.tax"].create( + { + "name": "Test Tax 10%", + "amount": 10.0, + "type_tax_use": "sale", + "company_id": cls.company.id, + "tax_group_id": tax_group.id, + } + ) + cls.env["account.journal"].create( + {"code": "SALE", "name": "Sales Journal", "type": "sale"} + ) + cls.bank_journal = cls.env["account.journal"].create( + { + "code": "BNK", + "name": "Bank Journal", + "type": "bank", + "default_account_id": account_bank.id, + "inbound_payment_method_line_ids": [ + Command.create( + { + "payment_method_id": cls.env.ref( + "account.account_payment_method_manual_in" + ).id, + "payment_account_id": account_bank.id, + } + ) + ], + } + ) + + def _create_invoice(self, amount, tax, move_type="out_invoice"): + invoice = self.env["account.move"].create( + { + "move_type": move_type, + "partner_id": self.partner.id, + "invoice_line_ids": [ + Command.create( + { + "name": "test line", + "account_id": self.account_income.id, + "quantity": 1, + "price_unit": amount, + "tax_ids": [Command.set(tax.ids)], + } + ) + ], + } + ) + invoice.action_post() + return invoice + + def _create_billing(self, amount_or_invoices, billing_date=None, validate=False): + """Create a billing. If amount is given, create an invoice first.""" + if isinstance(amount_or_invoices, int | float): + invoices = self._create_invoice(amount_or_invoices, self.tax_10) + else: + invoices = amount_or_invoices + billing = self.env["account.billing"].create( + { + "partner_id": self.partner.id, + "bill_type": "out_invoice", + "date": billing_date or date(2025, 1, 15), + "billing_line_ids": [ + Command.create({"move_id": inv.id}) for inv in invoices + ], + } + ) + if validate: + billing.validate_billing() + self.env.flush_all() + return billing + + def _register_payment(self, invoice, amount): + """Register a payment for an invoice.""" + payment = self.env["account.payment"].create( + { + "journal_id": self.bank_journal.id, + "partner_id": invoice.partner_id.id, + "amount": amount, + "payment_type": "inbound", + "partner_type": "customer", + } + ) + payment.action_post() + receivable_line = invoice.line_ids.filtered( + lambda x: x.account_id.account_type == "asset_receivable" + ) + payment_line = payment.move_id.line_ids.filtered( + lambda x: x.account_id.account_type == "asset_receivable" + ) + (receivable_line + payment_line).reconcile() + return payment + + def test_compute_prev_billing_id(self): + billing1 = self._create_billing(1000, date(2025, 1, 15), validate=True) + self.assertFalse(billing1.prev_billing_id) + billing2 = self._create_billing(2000, date(2025, 2, 15)) + self.assertEqual(billing2.prev_billing_id, billing1) + + def test_compute_carryover_amounts_no_previous(self): + billing = self._create_billing(1000) + self.assertEqual(billing.prev_billed_amount, 0) + self.assertEqual(billing.payment_amount, 0) + self.assertEqual(billing.carryover_amount, 0) + self.assertEqual(billing.total_billed_amount, 1100) + + def test_compute_carryover_amounts_with_previous(self): + self._create_billing(1000, date(2025, 1, 15), validate=True) + billing2 = self._create_billing(2000, date(2025, 2, 15)) + self.assertEqual(billing2.prev_billed_amount, 1100) + self.assertEqual(billing2._get_prev_billed_amount(), 1100) + self.assertEqual(billing2.payment_amount, 0) + self.assertEqual(billing2._get_payment_amount(), 0) + self.assertEqual(billing2.carryover_amount, 1100) + # Total billed amount is previous total plus current total + self.assertEqual(billing2.total_billed_amount, 3300) + + def test_compute_carryover_amounts_with_partial_payment(self): + billing1 = self._create_billing(1000, date(2025, 1, 15), validate=True) + self._register_payment(billing1.billing_line_ids.move_id, 500) + billing2 = self._create_billing(2000, date(2025, 2, 15)) + self.assertEqual(billing2.prev_billed_amount, 1100) + self.assertEqual(billing2.payment_amount, 500) + self.assertEqual(billing2.carryover_amount, 600) + + def test_compute_carryover_amounts_with_credit_note(self): + # Prev billing: invoice 1000 (1100 with tax) + credit note 200 (220 with tax) + # Net: 1100 - 220 = 880 + invoice = self._create_invoice(1000, self.tax_10) + credit_note = self._create_invoice(200, self.tax_10, move_type="out_refund") + billing1 = self._create_billing( + invoice + credit_note, date(2025, 1, 15), validate=True + ) + self.assertEqual(billing1.amount_total, 880) + billing2 = self._create_billing(2000, date(2025, 2, 15)) + self.assertEqual(billing2.prev_billed_amount, 880) + self.assertEqual(billing2.payment_amount, 0) + self.assertEqual(billing2.carryover_amount, 880) + + def test_manual_override_prev_billed_amount(self): + self._create_billing(1000, date(2025, 1, 15), validate=True) + billing2 = self._create_billing(2000, date(2025, 2, 15)) + billing2.use_prev_billed_amount_manual = True + billing2.prev_billed_amount_manual = 5000 + self.assertEqual(billing2.carryover_amount, 5000) + self.assertEqual(billing2._get_prev_billed_amount(), 5000) + # Test that zero override works (not treated as falsy) + billing2.prev_billed_amount_manual = 0 + self.assertEqual(billing2._get_prev_billed_amount(), 0) + self.assertEqual(billing2.carryover_amount, 0) + + def test_manual_override_payment_amount(self): + self._create_billing(1000, date(2025, 1, 15), validate=True) + billing2 = self._create_billing(2000, date(2025, 2, 15)) + billing2.use_payment_amount_manual = True + billing2.payment_amount_manual = 300 + self.assertEqual(billing2._get_payment_amount(), 300) + self.assertEqual(billing2.carryover_amount, 800) + + def test_prev_billing_candidates(self): + billing1 = self._create_billing(1000, date(2025, 1, 15), validate=True) + billing2 = self._create_billing(1500, date(2025, 2, 15), validate=True) + billing3 = self._create_billing(2000, date(2025, 3, 15)) + self.assertIn(billing1, billing3.prev_billing_candidate_ids) + self.assertIn(billing2, billing3.prev_billing_candidate_ids) + self.assertEqual(billing3.prev_billing_id, billing2) + + def test_validate_freezes_carryover_amounts(self): + """Validation copies computed values to manual fields to freeze them.""" + billing1 = self._create_billing(1000, date(2025, 1, 15), validate=True) + billing2 = self._create_billing(2000, date(2025, 2, 15)) + self.assertFalse(billing2.use_prev_billed_amount_manual) + self.assertFalse(billing2.use_payment_amount_manual) + self.assertEqual(billing2.carryover_amount, 1100) + billing2.validate_billing() + self.env.flush_all() + # Toggles are now on with frozen values + self.assertTrue(billing2.use_prev_billed_amount_manual) + self.assertTrue(billing2.use_payment_amount_manual) + self.assertEqual(billing2.prev_billed_amount_manual, 1100) + self.assertEqual(billing2.payment_amount_manual, 0) + # Register payment on billing1 after billing2 is validated + self._register_payment(billing1.billing_line_ids.move_id, 500) + # Carryover amounts remain frozen + self.assertEqual(billing2.carryover_amount, 1100) + + def test_carryover_accumulates_when_nothing_paid(self): + """Invoices from periods older than the previous one must keep being carried + over (regression for chains of 3+ unpaid periods).""" + self._create_billing(1000, date(2025, 1, 15), validate=True) + self._create_billing(2000, date(2025, 2, 15), validate=True) + # Period 3: prev = billing2, but nothing has been paid in any period. + billing3 = self._create_billing(3000, date(2025, 3, 15)) + # The whole 1100 (period 1) + 2200 (period 2) is still outstanding. + self.assertEqual(billing3.payment_amount, 0) + self.assertEqual(billing3.carryover_amount, 3300) + self.assertEqual(billing3.total_billed_amount, 6600) + + def test_carryover_counts_payment_on_older_billing(self): + """A payment on a billing older than the previous one is reflected in the + carryover of the current billing.""" + billing1 = self._create_billing(1000, date(2025, 1, 15), validate=True) + self._create_billing(2000, date(2025, 2, 15), validate=True) + # Pay period 1 in full before creating period 3. + self._register_payment(billing1.billing_line_ids.move_id, 1100) + billing3 = self._create_billing(3000, date(2025, 3, 15)) + # Only period 2's 2200 is still outstanding. + self.assertEqual(billing3.payment_amount, 1100) + self.assertEqual(billing3.carryover_amount, 2200) + self.assertEqual(billing3.total_billed_amount, 5500) + + def test_carryover_reacts_to_later_payment_on_older_billing(self): + """Paying a billing older than the immediate previous one, after the current + draft already exists, must still update its carryover (reactivity flows + through the recursive total_billed_amount dependency).""" + billing1 = self._create_billing(1000, date(2025, 1, 15), validate=True) + self._create_billing(2000, date(2025, 2, 15), validate=True) + billing3 = self._create_billing(3000, date(2025, 3, 15)) # draft + self.assertEqual(billing3.payment_amount, 0) + self.assertEqual(billing3.carryover_amount, 3300) + # Pay period 1 (older than billing3's previous billing) afterwards. + self._register_payment(billing1.billing_line_ids.move_id, 1100) + self.assertEqual(billing3.payment_amount, 1100) + self.assertEqual(billing3.carryover_amount, 2200) + self.assertEqual(billing3.total_billed_amount, 5500) + + def test_show_carryover_amounts_company_default(self): + """With the partner on 'default', the company setting decides, and a change + of the company setting refreshes existing billings.""" + self.partner.show_carryover_amounts = "default" + billing = self._create_billing(1000) + self.assertTrue(billing.show_carryover_amounts) + self.company.show_carryover_amounts = False + self.assertFalse(billing.show_carryover_amounts) + + def test_show_carryover_amounts_partner_override(self): + """An explicit partner setting overrides the company default and refreshes + existing billings when it changes.""" + billing = self._create_billing(1000) + self.partner.show_carryover_amounts = "no" + self.assertFalse(billing.show_carryover_amounts) + self.partner.show_carryover_amounts = "yes" + self.assertTrue(billing.show_carryover_amounts) + + def test_validate_billing_does_not_refreeze(self): + """Re-validating an already-validated billing must keep the frozen manual + values untouched.""" + self._create_billing(1000, date(2025, 1, 15), validate=True) + billing2 = self._create_billing(2000, date(2025, 2, 15), validate=True) + self.assertEqual(billing2.prev_billed_amount_manual, 1100) + self.assertEqual(billing2.payment_amount_manual, 0) + # Adjust the frozen values manually, then validate again. + billing2.prev_billed_amount_manual = 9999 + billing2.payment_amount_manual = 7777 + billing2.validate_billing() + self.env.flush_all() + self.assertEqual(billing2.prev_billed_amount_manual, 9999) + self.assertEqual(billing2.payment_amount_manual, 7777) diff --git a/l10n_jp_summary_invoice_carryover/views/account_billing_views.xml b/l10n_jp_summary_invoice_carryover/views/account_billing_views.xml new file mode 100644 index 00000000..9a8148bf --- /dev/null +++ b/l10n_jp_summary_invoice_carryover/views/account_billing_views.xml @@ -0,0 +1,67 @@ + + + + account.billing.form.carryover + account.billing + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/l10n_jp_summary_invoice_carryover/views/res_config_settings_views.xml b/l10n_jp_summary_invoice_carryover/views/res_config_settings_views.xml new file mode 100644 index 00000000..8620fd07 --- /dev/null +++ b/l10n_jp_summary_invoice_carryover/views/res_config_settings_views.xml @@ -0,0 +1,23 @@ + + + + res.config.settings.view.form - l10n_jp_summary_invoice_carryover + res.config.settings + + + + + + + + + + diff --git a/l10n_jp_summary_invoice_carryover/views/res_partner_views.xml b/l10n_jp_summary_invoice_carryover/views/res_partner_views.xml new file mode 100644 index 00000000..43287f18 --- /dev/null +++ b/l10n_jp_summary_invoice_carryover/views/res_partner_views.xml @@ -0,0 +1,18 @@ + + + + res.partner.form.carryover + res.partner + + + + + + + + + +