Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ADD] accountant: Added Custom duty on Import and Export Transactions #653

Draft
wants to merge 2 commits into
base: 18.0
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions accountant_custom_duty/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from . import models
from . import wizard
from . import tests
16 changes: 16 additions & 0 deletions accountant_custom_duty/__manifest__.py
Original file line number Diff line number Diff line change
@@ -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'
]
}
4 changes: 4 additions & 0 deletions accountant_custom_duty/models/__init__.py
Original file line number Diff line number Diff line change
@@ -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
57 changes: 57 additions & 0 deletions accountant_custom_duty/models/account_move.py
Original file line number Diff line number Diff line change
@@ -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")
29 changes: 29 additions & 0 deletions accountant_custom_duty/models/account_move_bill_of_entry_line.py
Original file line number Diff line number Diff line change
@@ -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)
17 changes: 17 additions & 0 deletions accountant_custom_duty/models/res_company.py
Original file line number Diff line number Diff line change
@@ -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)
52 changes: 52 additions & 0 deletions accountant_custom_duty/models/res_config_settings.py
Original file line number Diff line number Diff line change
@@ -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,
}
)
4 changes: 4 additions & 0 deletions accountant_custom_duty/security/ir.model.access.csv
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions accountant_custom_duty/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import test_account_move
83 changes: 83 additions & 0 deletions accountant_custom_duty/tests/test_account_move.py
Original file line number Diff line number Diff line change
@@ -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.")
Loading