diff --git a/accountant_custom_duty/__init__.py b/accountant_custom_duty/__init__.py new file mode 100644 index 00000000000..2c9b3790d63 --- /dev/null +++ b/accountant_custom_duty/__init__.py @@ -0,0 +1,3 @@ +from . import models +from . import wizard +from . import tests diff --git a/accountant_custom_duty/__manifest__.py b/accountant_custom_duty/__manifest__.py new file mode 100644 index 00000000000..e0e2e352384 --- /dev/null +++ b/accountant_custom_duty/__manifest__.py @@ -0,0 +1,16 @@ +{ + 'name': 'Custom duty on Import and Export', + 'version': '1.0', + 'license': 'LGPL-3', + 'depends': ['accountant', 'l10n_in'], + 'auto_install': True, + 'application': True, + 'data':[ + 'security/ir.model.access.csv', + + 'wizard/account_bill_of_entry_wizard.xml', + + 'views/account_move_views_inherit.xml', + 'views/res_config_settings_views.xml' + ] +} diff --git a/accountant_custom_duty/models/__init__.py b/accountant_custom_duty/models/__init__.py new file mode 100644 index 00000000000..74a00714f8c --- /dev/null +++ b/accountant_custom_duty/models/__init__.py @@ -0,0 +1,4 @@ +from . import res_company +from . import res_config_settings +from . import account_move +from . import account_move_bill_of_entry_line diff --git a/accountant_custom_duty/models/account_move.py b/accountant_custom_duty/models/account_move.py new file mode 100644 index 00000000000..ac0ef6b8dbe --- /dev/null +++ b/accountant_custom_duty/models/account_move.py @@ -0,0 +1,57 @@ +from odoo import fields, models + + +class AccountMove(models.Model): + _inherit = "account.move" + + is_confirmed = fields.Boolean(string="Confirmed", default=False) + l10n_in_custom_duty = fields.Boolean(related="company_id.l10n_in_custom_duty", readonly=False) + l10n_in_journal_entry_number = fields.Char(string="Journal Entry Number") + l10n_in_company_currency_id = fields.Many2one("res.currency", string="Company Currency ID", default=lambda self: self.env.ref("base.INR")) + l10n_in_custom_currency_rate = fields.Monetary(string="Custom Currency Rate", currency_field="l10n_in_company_currency_id") + l10n_in_reference = fields.Char(string="Bill Number") + l10n_in_total_custom_duty = fields.Monetary(string="Total Custom Duty + Additional Charges", currency_field="l10n_in_company_currency_id") + l10n_in_total_l10n_in_tax_amount = fields.Monetary(string="Total Tax Amount", currency_field="l10n_in_company_currency_id") + l10n_in_total_amount_payable = fields.Monetary(string="Total Amount Payable", currency_field="l10n_in_company_currency_id") + + bill_of_entry_line_ids = fields.One2many("account.move.bill.of.entry.line", "move_id", string="Bill of Entry Details") + + def action_open_wizard(self): + """Opens the Bill of Entry Wizard.""" + return { + "type": "ir.actions.act_window", + "name": "Bill of Entry Wizard", + "res_model": "account.bill.of.entry.wizard", + "view_mode": "form", + "target": "new", + "context": { + "default_move_id": self.id, + "default_l10n_in_reference": self.name, + "default_l10n_in_custom_duty_import_journal_id": self.env.company.l10n_in_custom_duty_import_journal_id.id, + "default_l10n_in_account_custom_duty_income_id": self.env.company.l10n_in_account_custom_duty_income_id.id, + "default_l10n_in_import_default_tax_account": self.env.company.l10n_in_import_default_tax_account.id, + "default_l10n_in_custom_duty_tax_payable_account": self.env.company.l10n_in_custom_duty_tax_payable_account.id, + "default_l10n_in_shipping_bill_number": self.l10n_in_shipping_bill_number, + "default_l10n_in_shipping_bill_date": self.l10n_in_shipping_bill_date, + "default_l10n_in_shipping_port_code_id": self.l10n_in_shipping_port_code_id, + }, + } + + def action_move_journal_line_bill_of_entry(self): + """Opens the related journal entry.""" + matching_move = self.env["account.move"].search([("name", "=", self.l10n_in_journal_entry_number)], limit=1) + if matching_move: + return { + "type": "ir.actions.act_window", + "res_model": "account.move", + "views": [[False, "form"]], + "res_id": matching_move.id, + } + +class AccountMoveLine(models.Model): + _inherit = "account.move.line" + + l10n_in_assessable_value = fields.Monetary(string="Assessable Value") + l10n_in_custom_duty_additional = fields.Monetary(string="Custom Duty + Additional Charges") + l10n_in_taxable_amount = fields.Monetary(string="Taxable Amount") + l10n_in_tax_amount = fields.Monetary(string="Tax Amount") diff --git a/accountant_custom_duty/models/account_move_bill_of_entry_line.py b/accountant_custom_duty/models/account_move_bill_of_entry_line.py new file mode 100644 index 00000000000..d82f04d6017 --- /dev/null +++ b/accountant_custom_duty/models/account_move_bill_of_entry_line.py @@ -0,0 +1,29 @@ +from odoo import fields, models + + +class AccountMoveBillOfEntryLine(models.Model): + _name = "account.move.bill.of.entry.line" + _description = "Bill of Entry Line" + + move_id = fields.Many2one("account.move", string="Journal Entry") + l10n_in_company_currency_id = fields.Many2one("res.currency", related="move_id.l10n_in_company_currency_id", string="Company Currency", readonly=True) + account_id = fields.Many2one("account.account", string="Account") + label = fields.Char(string="Label") + debit = fields.Monetary(string="Debit", currency_field="l10n_in_company_currency_id") + credit = fields.Monetary(string="Credit", currency_field="l10n_in_company_currency_id") + + product_id = fields.Many2one("product.product", string="Product") + name = fields.Char(string="Description") + quantity = fields.Float(string="Quantity") + price_unit = fields.Monetary(string="Unit Price", currency_field="l10n_in_company_currency_id") + tax_ids = fields.Many2many("account.tax", string="Taxes", domain=[("type_tax_use", "=", "purchase")]) + + l10n_in_custom_currency_rate = fields.Monetary(string="Custom Currency Rate", related="move_id.l10n_in_custom_currency_rate", currency_field="l10n_in_company_currency_id", readonly=True) + l10n_in_assessable_value = fields.Monetary(string="Assessable Value", store=True, currency_field="l10n_in_company_currency_id") + l10n_in_custom_duty_additional = fields.Monetary(string="Custom Duty + Additional Charges", currency_field="l10n_in_company_currency_id", help="Enter any additional custom duty charges manually.") + l10n_in_taxable_amount = fields.Monetary(string="Taxable Amount", store=True, currency_field="l10n_in_company_currency_id") + l10n_in_tax_amount = fields.Monetary(string="Tax Amount", store=True, currency_field="l10n_in_company_currency_id") + + l10n_in_shipping_bill_number = fields.Char(string="Shipping Bill Number", related="move_id.l10n_in_shipping_bill_number", readonly=True) + l10n_in_shipping_bill_date = fields.Date(string="Shipping Bill Date", related="move_id.l10n_in_shipping_bill_date", readonly=True) + l10n_in_shipping_port_code_id = fields.Many2one("l10n_in.port.code", string="Shipping Port Code", related="move_id.l10n_in_shipping_port_code_id", readonly=True) diff --git a/accountant_custom_duty/models/res_company.py b/accountant_custom_duty/models/res_company.py new file mode 100644 index 00000000000..5461092ec33 --- /dev/null +++ b/accountant_custom_duty/models/res_company.py @@ -0,0 +1,17 @@ +from odoo import fields, models + + +class ResCompany(models.Model): + _inherit = "res.company" + + l10n_in_custom_duty = fields.Boolean(string="Enable Custom Duty", default=False) + + l10n_in_custom_duty_import_journal_id = fields.Many2one(comodel_name="account.journal",string="Import Journal",check_company=True) + l10n_in_account_custom_duty_income_id = fields.Many2one(comodel_name="account.account",string="Import Custom Duty Income Account",check_company=True) + l10n_in_import_default_tax_account = fields.Many2one(comodel_name="account.account",string="Import Journal Suspense Account",check_company=True) + + l10n_in_custom_duty_export_journal_id = fields.Many2one(comodel_name="account.journal",string="Export Journal",check_company=True) + l10n_in_account_custom_duty_expense_income_id = fields.Many2one(comodel_name="account.account",string="Export Custom Duty Expense/Income Account",check_company=True) + l10n_in_export_default_tax_account = fields.Many2one(comodel_name="account.account",string="Export Journal Suspense Account",check_company=True) + + l10n_in_custom_duty_tax_payable_account = fields.Many2one(comodel_name="account.account",string="Custom Duty Tax Payable Account",check_company=True) diff --git a/accountant_custom_duty/models/res_config_settings.py b/accountant_custom_duty/models/res_config_settings.py new file mode 100644 index 00000000000..3ab56d65d36 --- /dev/null +++ b/accountant_custom_duty/models/res_config_settings.py @@ -0,0 +1,52 @@ +from odoo import api, fields, models + + +class ResConfigSettings(models.TransientModel): + _inherit = "res.config.settings" + + l10n_in_custom_duty = fields.Boolean(related="company_id.l10n_in_custom_duty", readonly=False, help="Enable Custom Duty.") + l10n_in_custom_duty_tax_payable_account = fields.Many2one(comodel_name="account.account", related="company_id.l10n_in_custom_duty_tax_payable_account", readonly=False, help="Import and Export tax payable account.") + + # Import + l10n_in_custom_duty_import_journal_id = fields.Many2one(comodel_name="account.journal", related="company_id.l10n_in_custom_duty_import_journal_id", readonly=False, help="Journal used for import custom duty.") + l10n_in_account_custom_duty_income_id = fields.Many2one(comodel_name="account.account", related="company_id.l10n_in_account_custom_duty_income_id", readonly=False, domain="[('account_type', '=', 'expense')]", help="Import custom duty account.") + l10n_in_import_default_tax_account = fields.Many2one(comodel_name="account.account", related="company_id.l10n_in_import_default_tax_account", readonly=False, domain="[('reconcile', '=', True), ('deprecated', '=', False), ('account_type', 'in', ('asset_current', 'liability_current'))]", help="Import account which is of type Current Asset or Liability.") + + # Export + l10n_in_custom_duty_export_journal_id = fields.Many2one(comodel_name="account.journal", related="company_id.l10n_in_custom_duty_export_journal_id", readonly=False, help="Journal used for export custom duty.") + l10n_in_account_custom_duty_expense_income_id = fields.Many2one(comodel_name="account.account", related="company_id.l10n_in_account_custom_duty_expense_income_id", readonly=False, domain="[('account_type', 'in', ('income', 'expense'))]", help="Export custom duty account.") + l10n_in_export_default_tax_account = fields.Many2one(comodel_name="account.account", related="company_id.l10n_in_export_default_tax_account", readonly=False, domain="[('reconcile', '=', True), ('deprecated', '=', False), ('account_type', 'in', ('asset_current', 'liability_current'))]", help="Export account which is of type Current Asset or Liability.") + + @api.model + def get_values(self): + res = super().get_values() + company = self.env.company + + res.update( + l10n_in_custom_duty=company.l10n_in_custom_duty, + l10n_in_custom_duty_import_journal_id=company.l10n_in_custom_duty_import_journal_id.id, + l10n_in_account_custom_duty_income_id=company.l10n_in_account_custom_duty_income_id.id, + l10n_in_import_default_tax_account=company.l10n_in_import_default_tax_account.id, + l10n_in_custom_duty_tax_payable_account=company.l10n_in_custom_duty_tax_payable_account.id, + l10n_in_custom_duty_export_journal_id=company.l10n_in_custom_duty_export_journal_id.id, + l10n_in_account_custom_duty_expense_income_id=company.l10n_in_account_custom_duty_expense_income_id.id, + l10n_in_export_default_tax_account=company.l10n_in_export_default_tax_account.id, + ) + return res + + def set_values(self): + super().set_values() + company = self.env.company + + company.write( + { + "l10n_in_custom_duty": self.l10n_in_custom_duty, + "l10n_in_custom_duty_import_journal_id": self.l10n_in_custom_duty_import_journal_id.id, + "l10n_in_account_custom_duty_income_id": self.l10n_in_account_custom_duty_income_id.id, + "l10n_in_import_default_tax_account": self.l10n_in_import_default_tax_account.id, + "l10n_in_custom_duty_tax_payable_account": self.l10n_in_custom_duty_tax_payable_account.id, + "l10n_in_custom_duty_export_journal_id": self.l10n_in_custom_duty_export_journal_id.id, + "l10n_in_account_custom_duty_expense_income_id": self.l10n_in_account_custom_duty_expense_income_id.id, + "l10n_in_export_default_tax_account": self.l10n_in_export_default_tax_account.id, + } + ) diff --git a/accountant_custom_duty/security/ir.model.access.csv b/accountant_custom_duty/security/ir.model.access.csv new file mode 100644 index 00000000000..a12519705be --- /dev/null +++ b/accountant_custom_duty/security/ir.model.access.csv @@ -0,0 +1,4 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_account_bill_of_entry_wizard,access_account_bill_of_entry_wizard,model_account_bill_of_entry_wizard,,1,1,1,0 +access_account_move_line_wizard,Access Account Move Line Wizard,model_account_move_line_wizard,,1,1,1,1 +access_account_move_bill_of_entry_line,access_account_move_bill_of_entry_line,model_account_move_bill_of_entry_line,,1,1,1,0 diff --git a/accountant_custom_duty/tests/__init__.py b/accountant_custom_duty/tests/__init__.py new file mode 100644 index 00000000000..f15da2000e3 --- /dev/null +++ b/accountant_custom_duty/tests/__init__.py @@ -0,0 +1 @@ +from . import test_account_move diff --git a/accountant_custom_duty/tests/test_account_move.py b/accountant_custom_duty/tests/test_account_move.py new file mode 100644 index 00000000000..dbe6f177043 --- /dev/null +++ b/accountant_custom_duty/tests/test_account_move.py @@ -0,0 +1,83 @@ +from odoo import fields +from odoo.tests import tagged, TransactionCase + + +@tagged('post_install', '-at_install') +class TestAccountMove(TransactionCase): + def setUp(self): + super().setUp() + self.partner_overseas = self.env['res.partner'].create({ + 'name': 'Overseas Supplier', + 'l10n_in_gst_treatment': 'overseas', + }) + + self.journal = self.env['account.journal'].search([('type', '=', 'purchase')], limit=1) + self.expense_account = self.env['account.account'].search([('account_type', '=', 'expense')], limit=1) + + self.bill = self.env['account.move'].create({ + 'move_type': 'in_invoice', + 'partner_id': self.partner_overseas.id, + 'invoice_date': fields.Date.today(), + 'journal_id': self.journal.id, + 'state': 'draft', + 'line_ids': [(0, 0, { + 'name': 'Test Product', + 'quantity': 1, + 'price_unit': 1000.0, + 'account_id': self.expense_account.id, + })] + }) + self.bill.action_post() + + def test_action_open_wizard(self): + """Test that the Bill of Entry Wizard opens with correct context.""" + action = self.bill.action_open_wizard() + self.assertEqual(action["res_model"], "account.bill.of.entry.wizard") + self.assertEqual(action["view_mode"], "form") + self.assertEqual(action["target"], "new") + self.assertEqual(action["context"]["default_move_id"], self.bill.id) + self.assertEqual(action["context"]["default_l10n_in_reference"], self.bill.name) + + def test_wizard_computations(self): + """Test computed fields in Account Move Line Wizard.""" + self.wizard = self.env['account.bill.of.entry.wizard'].create({ + 'move_id': self.bill.id, + 'l10n_in_custom_currency_rate': 1.2, + }) + + self.line = self.env['account.move.line.wizard'].create({ + 'wizard_id': self.wizard.id, + 'quantity': 10, + 'price_unit': 200, + 'l10n_in_custom_duty_additional': 50, + 'tax_ids': [(6, 0, self.env['account.tax'].search([('type_tax_use', '=', 'purchase')], limit=1).ids)], + }) + + self.line._compute_l10n_in_assessable_value() + self.line._compute_l10n_in_taxable_amount() + self.line._compute_l10n_in_tax_amount() + + expected_assessable_value = 10 * 200 * 1.2 + expected_taxable_amount = expected_assessable_value + 50 + expected_tax_amount = sum((expected_taxable_amount * tax.amount) / 100 for tax in self.line.tax_ids) + + self.assertEqual(self.line.l10n_in_assessable_value, expected_assessable_value, "Assessable Value computation is incorrect.") + self.assertEqual(self.line.l10n_in_taxable_amount, expected_taxable_amount, "Taxable Amount computation is incorrect.") + self.assertEqual(self.line.l10n_in_tax_amount, expected_tax_amount, "Total Tax Amount computation is incorrect.") + + def test_journal_entry_creation(self): + """Test that confirming the wizard creates a journal entry.""" + self.wizard = self.env['account.bill.of.entry.wizard'].create({ + 'move_id': self.bill.id, + 'l10n_in_custom_currency_rate': 1.2, + }) + + self.wizard.action_confirm() + journal_entry = self.env['account.move'].search([ + ('name', '=', self.bill.name), + ('journal_id', '=', self.journal.id), + ('state', '=', 'posted') + ], limit=1) + + self.assertTrue(journal_entry, "Journal entry was not created.") + self.assertEqual(journal_entry.state, 'posted', "Journal entry is not posted.") diff --git a/accountant_custom_duty/views/account_move_views_inherit.xml b/accountant_custom_duty/views/account_move_views_inherit.xml new file mode 100644 index 00000000000..90dcf371b23 --- /dev/null +++ b/accountant_custom_duty/views/account_move_views_inherit.xml @@ -0,0 +1,78 @@ + + + + + account.move.form.import.export.custom.duty + account.move + + + + + + + + + Bill of Entry + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + not (not l10n_in_custom_currency_rate or move_type in ('out_invoice', 'out_refund', 'in_invoice', 'in_refund', 'out_receipt', 'in_receipt') or state == 'draft') + + + not (not l10n_in_custom_currency_rate or move_type in ('out_invoice', 'out_refund', 'in_invoice', 'in_refund', 'out_receipt', 'in_receipt') or state == 'draft') + + + not (not l10n_in_custom_currency_rate or move_type in ('out_invoice', 'out_refund', 'in_invoice', 'in_refund', 'out_receipt', 'in_receipt') or state == 'draft') + + + (l10n_in_custom_currency_rate or move_type in ('out_invoice', 'out_refund', 'in_invoice', 'in_refund', 'out_receipt', 'in_receipt') or state == 'draft') + + + + + diff --git a/accountant_custom_duty/views/res_config_settings_views.xml b/accountant_custom_duty/views/res_config_settings_views.xml new file mode 100644 index 00000000000..53e0d0cd344 --- /dev/null +++ b/accountant_custom_duty/views/res_config_settings_views.xml @@ -0,0 +1,52 @@ + + + + res.config.settings.view.form.inherit.account.base.import.export.custom.duty + res.config.settings + + + + + + + + Import: + + + + + + + + + + + + + + Export: + + + + + + + + + + + + + + Default Tax Payable Account: + + + + + + + + + + + diff --git a/accountant_custom_duty/wizard/__init__.py b/accountant_custom_duty/wizard/__init__.py new file mode 100644 index 00000000000..976866c06e6 --- /dev/null +++ b/accountant_custom_duty/wizard/__init__.py @@ -0,0 +1,2 @@ +from . import account_bill_of_entry_wizard +from . import account_move_line_wizard diff --git a/accountant_custom_duty/wizard/account_bill_of_entry_wizard.py b/accountant_custom_duty/wizard/account_bill_of_entry_wizard.py new file mode 100644 index 00000000000..c14a8f8b180 --- /dev/null +++ b/accountant_custom_duty/wizard/account_bill_of_entry_wizard.py @@ -0,0 +1,207 @@ +from odoo import api, fields, models + + +class CustomWizard(models.TransientModel): + _name = "account.bill.of.entry.wizard" + _description = "Custom Wizard" + + l10n_in_journal_entry_date = fields.Date(string="Journal Entry Date", default=fields.Date.today()) + l10n_in_custom_duty_import_journal_id = fields.Many2one("account.journal", string="Journal") + l10n_in_account_custom_duty_income_id = fields.Many2one(comodel_name="account.account", string="Separate account for income discount") + + l10n_in_import_default_tax_account = fields.Many2one("account.account", string="Journal Suspense Account", check_company=True) + l10n_in_custom_duty_tax_payable_account = fields.Many2one("account.account", string="Custom Duty Tax Payable Account", check_company=True) + + l10n_in_company_currency_id = fields.Many2one("res.currency", string="Company Currency ID", default=lambda self: self.env.ref("base.INR")) + l10n_in_custom_currency_rate = fields.Monetary(string="Custom Currency Rate", currency_field="l10n_in_company_currency_id", default=1.0) + + l10n_in_reference = fields.Char(string="Bill Number", help="Bill No. on which this model is accessed", readonly=True) + l10n_in_journal_entry_number = fields.Char(string="Journal Entry Number", compute="_compute_l10n_in_journal_entry_number", store=True) + + l10n_in_shipping_bill_number = fields.Char("Bill of Entry No.") + l10n_in_shipping_bill_date = fields.Date("Bill of Entry Date") + l10n_in_shipping_port_code_id = fields.Many2one("l10n_in.port.code", "Port code") + + l10n_in_move_line_ids = fields.One2many("account.move.line.wizard", "wizard_id", string="Product Lines") + + l10n_in_total_custom_duty = fields.Monetary(string="Total Custom Duty + Additional Charges", compute="_compute_l10n_in_total_custom_duty", store=True, currency_field="l10n_in_company_currency_id") + + l10n_in_total_l10n_in_tax_amount = fields.Monetary(string="Total Tax Amount", compute="_compute_l10n_in_total_l10n_in_tax_amount", store=True, currency_field="l10n_in_company_currency_id") + + l10n_in_total_amount_payable = fields.Monetary(string="Total Amount Payable", compute="_compute_l10n_in_total_amount_payable", store=True, currency_field="l10n_in_company_currency_id") + l10n_in_journal_item_ids = fields.One2many("account.move.line.wizard", "wizard_id", compute="_compute_journal_items", string="Journal Items") + move_id = fields.Many2one("account.move", string="Journal Entry", required=True) + + @api.depends("l10n_in_custom_duty_import_journal_id") + def _compute_l10n_in_journal_entry_number(self): + """Compute the next journal entry number based on the selected journal.""" + for wizard in self: + if wizard.l10n_in_custom_duty_import_journal_id: + wizard.l10n_in_journal_entry_number = wizard._get_next_sequence(wizard.l10n_in_custom_duty_import_journal_id) + + def _get_next_sequence(self, journal): + """Fetch the next sequence number for the provided journal.""" + last_move = self.env["account.move"].search([("journal_id", "=", journal.id)], order="name desc", limit=1) + last_sequence = last_move.name if last_move else None + + today = fields.Date.today() + fy_start = today.year if today.month >= 4 else today.year - 1 + fy_end = fy_start + 1 + fy_format = f"{str(fy_end)}/{today.month:02d}" + + if last_sequence and last_sequence.split("/")[2] == f"{today.month:02d}": + parts = last_sequence.split("/") + last_number = int(parts[-1]) + new_number = str(last_number + 1).zfill(4) + parts[-1] = new_number + return "/".join(parts) + else: + return f"{journal.code}/{fy_format}/0001" + + @api.depends("l10n_in_total_custom_duty", "l10n_in_total_l10n_in_tax_amount", "l10n_in_total_amount_payable") + def _compute_journal_items(self): + for wizard in self: + journal_items = [ + (0,0, + { + "account_id": wizard.env.company.l10n_in_account_custom_duty_income_id.id, + "label": "Custom Duty Account", + "debit": wizard.l10n_in_total_custom_duty, + "credit": 0.0, + }, + ), + (0,0, + { + "account_id": wizard.env.company.l10n_in_import_default_tax_account.id, + "label": "IGST on Import Account", + "debit": wizard.l10n_in_total_l10n_in_tax_amount, + "credit": 0.0, + }, + ), + (0,0, + { + "account_id": wizard.env.company.l10n_in_custom_duty_tax_payable_account.id, + "label": "Custom Duty Tax Payable Account", + "debit": 0.0, + "credit": wizard.l10n_in_total_amount_payable, + }, + ), + ] + wizard.l10n_in_journal_item_ids = journal_items + + @api.depends("l10n_in_move_line_ids.l10n_in_custom_duty_additional") + def _compute_l10n_in_total_custom_duty(self): + for wizard in self: + wizard.l10n_in_total_custom_duty = sum( + wizard.l10n_in_move_line_ids.mapped("l10n_in_custom_duty_additional") + ) + + @api.depends("l10n_in_move_line_ids.l10n_in_tax_amount") + def _compute_l10n_in_total_l10n_in_tax_amount(self): + for wizard in self: + wizard.l10n_in_total_l10n_in_tax_amount = sum( + wizard.l10n_in_move_line_ids.mapped("l10n_in_tax_amount") + ) + + @api.depends("l10n_in_move_line_ids.l10n_in_custom_duty_additional", "l10n_in_move_line_ids.l10n_in_tax_amount") + def _compute_l10n_in_total_amount_payable(self): + for wizard in self: + wizard.l10n_in_total_amount_payable = (wizard.l10n_in_total_custom_duty + wizard.l10n_in_total_l10n_in_tax_amount) + + @api.model + def default_get(self, fields_list): + defaults = super().default_get(fields_list) + company = self.env.company + defaults["l10n_in_custom_duty_import_journal_id"] = (company.l10n_in_custom_duty_import_journal_id.id) + defaults["l10n_in_account_custom_duty_income_id"] = (company.l10n_in_account_custom_duty_income_id.id) + defaults["l10n_in_import_default_tax_account"] = (company.l10n_in_import_default_tax_account.id) + defaults["l10n_in_custom_duty_tax_payable_account"] = (company.l10n_in_custom_duty_tax_payable_account.id) + + move_id = self._context.get("default_move_id") + if move_id: + move = self.env["account.move"].browse(move_id) + lines = [] + defaults["l10n_in_shipping_port_code_id"] = ( + move.l10n_in_shipping_port_code_id.id + ) + for line in move.invoice_line_ids: + lines.append( + (0,0, + { + "wizard_id": self.id, + "product_id": line.product_id.id, + "name": line.name, + "quantity": line.quantity, + "price_unit": line.price_unit, + "account_id": line.account_id.id, + "tax_ids": [(6, 0, line.tax_ids.ids)], + "l10n_in_assessable_value": line.l10n_in_assessable_value, + "l10n_in_custom_duty_additional": line.l10n_in_custom_duty_additional, + "l10n_in_taxable_amount": line.l10n_in_taxable_amount, + "l10n_in_tax_amount": line.l10n_in_tax_amount, + }, + ) + ) + + defaults["l10n_in_move_line_ids"] = lines + + return defaults + + def action_confirm(self): + move_vals = { + "date": self.l10n_in_journal_entry_date, + "journal_id": self.l10n_in_custom_duty_import_journal_id.id, + "l10n_in_custom_currency_rate": self.l10n_in_custom_currency_rate, + "l10n_in_total_custom_duty": self.l10n_in_total_custom_duty, + "l10n_in_total_l10n_in_tax_amount": self.l10n_in_total_l10n_in_tax_amount, + "l10n_in_total_amount_payable": self.l10n_in_total_amount_payable, + "l10n_in_shipping_bill_number": self.l10n_in_shipping_bill_number, + "l10n_in_shipping_bill_date": self.l10n_in_shipping_bill_date, + "l10n_in_shipping_port_code_id": self.l10n_in_shipping_port_code_id.id, + "line_ids": [] + } + + move = self.env["account.move"].create(move_vals) + + line_ids = [] + for line in self.l10n_in_journal_item_ids: + line_ids.append( + (0,0, + { + "account_id": line.account_id.id, + "name": line.label, + "debit": line.debit, + "credit": line.credit, + }, + ) + ) + move.write({"line_ids": line_ids, "company_currency_id": self.l10n_in_company_currency_id.id}) + + bill_of_entry_line_ids = [] + for line in self.l10n_in_move_line_ids: + bill_of_entry_line_ids.append( + (0,0, + { + "account_id": line.account_id.id, + "name": line.label or "Bill Entry", + "product_id": line.product_id.id or False, + "quantity": line.quantity or 1.0, + "price_unit": line.price_unit * self.l10n_in_custom_currency_rate, + "tax_ids": [(6, 0, line.tax_ids.ids)] if line.tax_ids else [], + "l10n_in_assessable_value": line.l10n_in_assessable_value, + "l10n_in_custom_duty_additional": line.l10n_in_custom_duty_additional, + "l10n_in_taxable_amount": line.l10n_in_taxable_amount, + "l10n_in_tax_amount": line.l10n_in_tax_amount, + }) + ) + move.write({"bill_of_entry_line_ids": bill_of_entry_line_ids}) + + + self.move_id.write( + { + "l10n_in_journal_entry_number": self.l10n_in_journal_entry_number, + } + ) + move.action_post() + self.move_id.is_confirmed = True + return {"type": "ir.actions.act_window_close"} diff --git a/accountant_custom_duty/wizard/account_bill_of_entry_wizard.xml b/accountant_custom_duty/wizard/account_bill_of_entry_wizard.xml new file mode 100644 index 00000000000..dc9d2c2a0a0 --- /dev/null +++ b/accountant_custom_duty/wizard/account_bill_of_entry_wizard.xml @@ -0,0 +1,69 @@ + + + + + account.bill.of.entry.wizard.form + account.bill.of.entry.wizard + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Custom Wizard + account.bill.of.entry.wizard + form + new + + + diff --git a/accountant_custom_duty/wizard/account_move_line_wizard.py b/accountant_custom_duty/wizard/account_move_line_wizard.py new file mode 100644 index 00000000000..55a4b17f725 --- /dev/null +++ b/accountant_custom_duty/wizard/account_move_line_wizard.py @@ -0,0 +1,44 @@ +from odoo import api, fields, models + + +class AccountMoveLineWizard(models.TransientModel): + _name = "account.move.line.wizard" + _description = "Account Move Line Wizard" + + wizard_id = fields.Many2one("account.bill.of.entry.wizard", string="Wizard", required=True, ondelete="cascade") + l10n_in_company_currency_id = fields.Many2one("res.currency", related="wizard_id.l10n_in_company_currency_id", string="Company Currency", readonly=True) + account_id = fields.Many2one("account.account", string="Account") + label = fields.Char(string="Label") + debit = fields.Monetary(string="Debit", currency_field="l10n_in_company_currency_id") + credit = fields.Monetary(string="Credit", currency_field="l10n_in_company_currency_id") + + product_id = fields.Many2one("product.product", string="Product") + name = fields.Char(string="Description") + quantity = fields.Float(string="Quantity") + price_unit = fields.Monetary(string="Unit Price", currency_field="l10n_in_company_currency_id") + tax_ids = fields.Many2many("account.tax", string="Taxes", domain=[("type_tax_use", "=", "purchase")]) + + l10n_in_custom_currency_rate = fields.Monetary(string="Custom Currency Rate", related="wizard_id.l10n_in_custom_currency_rate", currency_field="l10n_in_company_currency_id", readonly=True) + + l10n_in_assessable_value = fields.Monetary(string="Assessable Value", compute="_compute_l10n_in_assessable_value", store=True, currency_field="l10n_in_company_currency_id") + + l10n_in_custom_duty_additional = fields.Monetary(string="Custom Duty + Additional Charges", currency_field="l10n_in_company_currency_id", help="Enter any additional custom duty charges manually.") + + l10n_in_taxable_amount = fields.Monetary(string="Taxable Amount", compute="_compute_l10n_in_taxable_amount", store=True, currency_field="l10n_in_company_currency_id") + + l10n_in_tax_amount = fields.Monetary(string="Tax Amount", compute="_compute_l10n_in_tax_amount", store=True, currency_field="l10n_in_company_currency_id") + + @api.depends("quantity", "price_unit", "l10n_in_custom_currency_rate") + def _compute_l10n_in_assessable_value(self): + for line in self: + line.l10n_in_assessable_value = (line.quantity * line.price_unit * line.l10n_in_custom_currency_rate) + + @api.depends("l10n_in_assessable_value", "l10n_in_custom_duty_additional") + def _compute_l10n_in_taxable_amount(self): + for line in self: + line.l10n_in_taxable_amount = (line.l10n_in_assessable_value + line.l10n_in_custom_duty_additional) + + @api.depends("l10n_in_taxable_amount", "tax_ids") + def _compute_l10n_in_tax_amount(self): + for line in self: + line.l10n_in_tax_amount = sum((line.l10n_in_taxable_amount * tax.amount) / 100 for tax in line.tax_ids)
Import:
Export:
Default Tax Payable Account: