summaryrefslogtreecommitdiff
path: root/addons/l10n_in/models
diff options
context:
space:
mode:
authorJigar Vaghela <jva@odoo.com>2018-06-21 09:07:54 +0000
committerqdp-odoo <qdp@odoo.com>2019-01-18 08:35:33 +0000
commit451fd6cc90aaa14816376a0285334678df9ce484 (patch)
tree034e18752050b9e92a7cdcdf3e46c1ceed8c0cc6 /addons/l10n_in/models
parent8c0d62082f6eb71ec106126db2f7f046a9b8b1b5 (diff)
[IMP] l10n_in: Improve Indian localization to supports GSTR-1 reports
Purpose ======= GST Invoice Report required to show tax amount product line wise. This Tax amount is divided in CESS and IGST or CGST and SGST. Improve GST Tax, Tax Groups and Tag. Reverse charge under GST As per GST rules tax will have to be paid directly by the receiver to the Government instead of the supplier. Reseller(E-commerce) under GST If you sale through ecommerce then need to specify ecommerce GSTIN. Import/Export goods under GST If you export the goods then need to specify -Export Type -Shipping bill number -Shipping bill date -Shipping port code Credit or Debit Note If u give Credit or Debit note then need to specify Refund reason. Place of Supply is the state of customer. Added GSTR reports GSTR Invoice report covers below GSTR-1 sections(this report is grouped by tax rate and journal entries B2B, B2CL, B2CS, EXP, CDNR and CDNUR GSTR payment report This report is covered under GSTIR-1 section called AT(advance payment) and ATADJ(advance payment adjustment) GSTR HSN report(it is based on the product HSN code) GSTR Exempted report(it is based on nil rated and exempt tax) This report is covered under GSTIR-1 section EXEMP Added demo data to to have GSTR-1 reports out of the box Solution ======== fetched values of CESS, IGST, CGST and SGST amount in invoice report from tax groups added new taxes called "Nil Rated" and "Exempt" and also added it's related tax groups and accounts added tax tags related to GST tax percentage Set Reverse charge in negative tax Improved account move line creation grouping machenism: Now, move lines related to tax are grouped by product and uom instead of grouping them just by tax(this is for better reporting) closes odoo/odoo#30332
Diffstat (limited to 'addons/l10n_in/models')
-rw-r--r--addons/l10n_in/models/__init__.py5
-rw-r--r--addons/l10n_in/models/account.py40
-rw-r--r--addons/l10n_in/models/account_invoice.py62
-rw-r--r--addons/l10n_in/models/chart_template.py32
-rw-r--r--addons/l10n_in/models/port_code.py19
-rw-r--r--addons/l10n_in/models/product_template.py1
-rw-r--r--addons/l10n_in/models/res_config_settings.py15
-rw-r--r--addons/l10n_in/models/res_partner.py17
-rw-r--r--addons/l10n_in/models/uom_uom.py11
9 files changed, 189 insertions, 13 deletions
diff --git a/addons/l10n_in/models/__init__.py b/addons/l10n_in/models/__init__.py
index a4c946eba25..2f479b39847 100644
--- a/addons/l10n_in/models/__init__.py
+++ b/addons/l10n_in/models/__init__.py
@@ -1,7 +1,12 @@
# -*- coding:utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
+from . import account
from . import account_invoice
from . import chart_template
from . import product_template
+from . import port_code
+from . import res_config_settings
from . import res_country_state
+from . import res_partner
+from . import uom_uom
diff --git a/addons/l10n_in/models/account.py b/addons/l10n_in/models/account.py
new file mode 100644
index 00000000000..b28d6a1ab33
--- /dev/null
+++ b/addons/l10n_in/models/account.py
@@ -0,0 +1,40 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo import api, fields, models, _
+from odoo.exceptions import ValidationError
+
+
+class AccountJournal(models.Model):
+ _inherit = "account.journal"
+
+ # Use for filter import and export type.
+ l10n_in_import_export = fields.Boolean("Import/Export", help="Tick this if this journal is use for Import/Export Under Indian GST.")
+
+
+class AccountMoveLine(models.Model):
+ _inherit = "account.move.line"
+
+ @api.depends('move_id.line_ids', 'move_id.line_ids.tax_line_id', 'move_id.line_ids.debit', 'move_id.line_ids.credit')
+ def _compute_tax_base_amount(self):
+ aml = self.filtered(lambda l: l.company_id.country_id.code == 'IN' and l.tax_line_id and l.product_id)
+ for move_line in aml:
+ base_lines = move_line.move_id.line_ids.filtered(lambda line: move_line.tax_line_id in line.tax_ids and move_line.product_id == line.product_id)
+ move_line.tax_base_amount = abs(sum(base_lines.mapped('balance')))
+ remaining_aml = self - aml
+ if remaining_aml:
+ return super(AccountMoveLine, remaining_aml)._compute_tax_base_amount()
+
+
+class AccountTax(models.Model):
+ _inherit = 'account.tax'
+
+ l10n_in_reverse_charge = fields.Boolean("Reverse charge", help="Tick this if this tax is reverse charge. Only for Indian accounting")
+
+ def get_grouping_key(self, invoice_tax_val):
+ """ Returns a string that will be used to group account.invoice.tax sharing the same properties"""
+ key = super(AccountTax, self).get_grouping_key(invoice_tax_val)
+ if self.company_id.country_id.code == 'IN':
+ key += "-%s-%s"% (invoice_tax_val.get('l10n_in_product_id', False),
+ invoice_tax_val.get('l10n_in_uom_id', False))
+ return key
diff --git a/addons/l10n_in/models/account_invoice.py b/addons/l10n_in/models/account_invoice.py
index 2bd7475fc31..7d5317c77b1 100644
--- a/addons/l10n_in/models/account_invoice.py
+++ b/addons/l10n_in/models/account_invoice.py
@@ -2,10 +2,10 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, fields, models, _
+from odoo.exceptions import ValidationError
class AccountInvoice(models.Model):
-
_inherit = "account.invoice"
@api.depends('amount_total')
@@ -14,6 +14,21 @@ class AccountInvoice(models.Model):
invoice.amount_total_words = invoice.currency_id.amount_to_text(invoice.amount_total)
amount_total_words = fields.Char("Total (In Words)", compute="_compute_amount_total_words")
+ # Use for invisible fields in form views.
+ l10n_in_import_export = fields.Boolean(related='journal_id.l10n_in_import_export', readonly=True)
+ # For Export invoice this data is need in GSTR report
+ l10n_in_export_type = fields.Selection([
+ ('regular', 'Regular'), ('deemed', 'Deemed'),
+ ('sale_from_bonded_wh', 'Sale from Bonded WH'),
+ ('export_with_igst', 'Export with IGST'),
+ ('sez_with_igst', 'SEZ with IGST payment'),
+ ('sez_without_igst', 'SEZ without IGST payment')],
+ string='Export Type', default='regular', required=True)
+ l10n_in_shipping_bill_number = fields.Char('Shipping bill number', readonly=True, states={'draft': [('readonly', False)]})
+ l10n_in_shipping_bill_date = fields.Date('Shipping bill date', readonly=True, states={'draft': [('readonly', False)]})
+ l10n_in_shipping_port_code_id = fields.Many2one('l10n_in.port.code', 'Shipping port code', states={'draft': [('readonly', False)]})
+ l10n_in_reseller_partner_id = fields.Many2one('res.partner', 'Reseller', domain=[('vat', '!=', False)], help="Only Registered Reseller", readonly=True, states={'draft': [('readonly', False)]})
+ l10n_in_partner_vat = fields.Char(related="partner_id.vat", readonly=True)
def _get_report_base_filename(self):
self.ensure_one()
@@ -33,7 +48,6 @@ class AccountInvoice(models.Model):
self.ensure_one()
tax_datas = {}
TAX = self.env['account.tax']
-
for line in self.mapped('invoice_line_ids'):
price_unit = line.price_unit * (1 - (line.discount or 0.0) / 100.0)
tax_lines = line.invoice_line_tax_ids.compute_all(price_unit, line.invoice_id.currency_id, line.quantity, line.product_id, line.invoice_id.partner_id)['taxes']
@@ -41,3 +55,47 @@ class AccountInvoice(models.Model):
tax_line['tag_ids'] = TAX.browse(tax_line['id']).tag_ids.ids
tax_datas[line.id] = tax_lines
return tax_datas
+
+ def inv_line_characteristic_hashcode(self, invoice_line):
+ res = super(AccountInvoice, self).inv_line_characteristic_hashcode(invoice_line)
+ return res + "-%s" %(invoice_line.get('product_uom_id', 'False'))
+
+ @api.model
+ def tax_line_move_line_get(self):
+ res = super(AccountInvoice, self).tax_line_move_line_get()
+ for vals in res:
+ invoice_tax_line = self.env['account.invoice.tax'].browse(vals.get('invoice_tax_line_id'))
+ vals['product_id'] = invoice_tax_line.l10n_in_product_id.id
+ vals['uom_id'] = invoice_tax_line.l10n_in_uom_id.id
+ vals['quantity'] = invoice_tax_line.l10n_in_quantity
+ return res
+
+ def _prepare_tax_line_vals(self, line, tax):
+ vals = super(AccountInvoice, self)._prepare_tax_line_vals(line, tax)
+ vals['l10n_in_product_id'] = line.product_id.id
+ vals['l10n_in_uom_id'] = line.uom_id.id
+ vals['l10n_in_quantity'] = line.quantity
+ return vals
+
+ @api.multi
+ def get_taxes_values(self, tax_group_fields=False):
+ if tax_group_fields:
+ tax_group_fields |= set(['l10n_in_quantity'])
+ else:
+ tax_group_fields = set(['l10n_in_quantity'])
+ return super(AccountInvoice, self).get_taxes_values(tax_group_fields)
+
+
+class AccountInvoiceTax(models.Model):
+ _inherit = "account.invoice.tax"
+
+ l10n_in_product_id = fields.Many2one('product.product', string='Product')
+ l10n_in_uom_id = fields.Many2one('uom.uom', string='Unit of Measure')
+ l10n_in_quantity = fields.Float(string='Quantity')
+
+ @api.multi
+ def _prepare_invoice_tax_val(self):
+ res = super(AccountInvoiceTax, self)._prepare_invoice_tax_val()
+ res['l10n_in_product_id'] = self.l10n_in_product_id.id
+ res['l10n_in_uom_id'] = self.l10n_in_uom_id.id
+ return res
diff --git a/addons/l10n_in/models/chart_template.py b/addons/l10n_in/models/chart_template.py
index 275087c7dbc..bd8b66d3b8c 100644
--- a/addons/l10n_in/models/chart_template.py
+++ b/addons/l10n_in/models/chart_template.py
@@ -1,23 +1,33 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
-from odoo import api, models, _
+from odoo import api, fields, models, _
class AccountChartTemplate(models.Model):
_inherit = 'account.chart.template'
-
@api.multi
def _prepare_all_journals(self, acc_template_ref, company, journals_dict=None):
- res = super(AccountChartTemplate, self)._prepare_all_journals(
- acc_template_ref, company, journals_dict=journals_dict)
- if not self == self.env.ref('l10n_in.indian_chart_template_standard'):
- return res
- for journal in res:
- if journal['code'] == 'INV':
- journal['name'] = _('Tax Invoices')
+ res = super(AccountChartTemplate, self)._prepare_all_journals(acc_template_ref, company, journals_dict=journals_dict)
+ if self == self.env.ref('l10n_in.indian_chart_template_standard'):
+ for journal in res:
+ if journal['code'] == 'INV':
+ journal['name'] = _('Tax Invoices')
- res += [{'type': 'sale', 'name': _('Retail Invoices'), 'code': 'RETINV', 'company_id': company.id, 'show_on_dashboard': True,},
- {'type': 'sale', 'name': _('Export Invoices'), 'code': 'EXPINV', 'company_id': company.id, 'show_on_dashboard': True,},]
+ res += [
+ {'type': 'sale', 'name': _('Retail Invoices'), 'code': 'RETINV', 'company_id': company.id, 'show_on_dashboard': True,},
+ {'type': 'sale', 'name': _('Export Invoices'), 'code': 'EXPINV', 'company_id': company.id, 'show_on_dashboard': True, 'l10n_in_import_export': True}
+ ]
return res
+
+class AccountTaxTemplate(models.Model):
+ _inherit = 'account.tax.template'
+
+ l10n_in_reverse_charge = fields.Boolean("Reverse charge", help="Tick this if this tax is reverse charge. Only for Indian accounting")
+
+ def _get_tax_vals(self, company, tax_template_to_tax):
+ val = super(AccountTaxTemplate, self)._get_tax_vals(company, tax_template_to_tax)
+ if self.tax_group_id:
+ val['l10n_in_reverse_charge'] = self.l10n_in_reverse_charge
+ return val
diff --git a/addons/l10n_in/models/port_code.py b/addons/l10n_in/models/port_code.py
new file mode 100644
index 00000000000..dfd54f34d18
--- /dev/null
+++ b/addons/l10n_in/models/port_code.py
@@ -0,0 +1,19 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo import fields, models
+
+
+class L10nInPortCode(models.Model):
+ """Port code must be mentioned in export and import of goods under GST."""
+ _name = 'l10n_in.port.code'
+ _description = "Indian port code"
+ _rec_name = 'code'
+
+ code = fields.Char(string="Port Code", required=True)
+ name = fields.Char(string="Port", required=True)
+ state_id = fields.Many2one('res.country.state', string="State")
+
+ _sql_constraints = [
+ ('code_uniq', 'unique (code)', 'The Port Code must be unique!')
+ ]
diff --git a/addons/l10n_in/models/product_template.py b/addons/l10n_in/models/product_template.py
index 0a8b47196e9..6250fac27ed 100644
--- a/addons/l10n_in/models/product_template.py
+++ b/addons/l10n_in/models/product_template.py
@@ -8,3 +8,4 @@ class ProductTemplate(models.Model):
_inherit = 'product.template'
l10n_in_hsn_code = fields.Char(string="HSN/SAC Code", help="Harmonized System Nomenclature/Services Accounting Code")
+ l10n_in_hsn_description = fields.Char(string="HSN/SAC Description", help="HSN/SAC description is required if HSN/SAC code is not provided.")
diff --git a/addons/l10n_in/models/res_config_settings.py b/addons/l10n_in/models/res_config_settings.py
new file mode 100644
index 00000000000..eb813ff1960
--- /dev/null
+++ b/addons/l10n_in/models/res_config_settings.py
@@ -0,0 +1,15 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo import api, fields, models
+
+
+class ResConfigSettings(models.TransientModel):
+ _inherit = 'res.config.settings'
+
+ show_module_l10n_in = fields.Boolean(compute='_compute_show_module_l10n_in')
+ group_l10n_in_reseller = fields.Boolean(implied_group='l10n_in.group_l10n_in_reseller', string="Manage Reseller(E-Commerce)")
+
+ @api.depends('company_id')
+ def _compute_show_module_l10n_in(self):
+ self.show_module_l10n_in = self.company_id.country_id.code == 'IN'
diff --git a/addons/l10n_in/models/res_partner.py b/addons/l10n_in/models/res_partner.py
new file mode 100644
index 00000000000..d1c5a27d9a9
--- /dev/null
+++ b/addons/l10n_in/models/res_partner.py
@@ -0,0 +1,17 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo import api, fields, models, _
+from odoo.exceptions import ValidationError
+
+
+class ResPartner(models.Model):
+ _inherit = 'res.partner'
+
+ # Use in view attrs. Need to required state_id if Country is India.
+ l10n_in_country_code = fields.Char(related="country_id.code", string="Country code")
+
+ @api.constrains('vat', 'country_id')
+ def l10n_in_check_vat(self):
+ for partner in self.filtered(lambda p: p.commercial_partner_id.country_id.code == 'IN' and p.vat and len(p.vat) != 15):
+ raise ValidationError(_('The GSTIN [%s] for partner [%s] should be 15 characters only.') % (partner.vat, partner.name))
diff --git a/addons/l10n_in/models/uom_uom.py b/addons/l10n_in/models/uom_uom.py
new file mode 100644
index 00000000000..b9b3fcc5e30
--- /dev/null
+++ b/addons/l10n_in/models/uom_uom.py
@@ -0,0 +1,11 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo import fields, models
+
+
+class UoM(models.Model):
+ _inherit = "uom.uom"
+
+ # As per GST Rules you need to Specify UQC given by GST.
+ l10n_in_code = fields.Char("Indian GST UQC", help="Unique Quantity Code (UQC) under GST")