diff options
| author | Jigar Vaghela <jva@odoo.com> | 2018-06-21 09:07:54 +0000 |
|---|---|---|
| committer | qdp-odoo <qdp@odoo.com> | 2019-01-18 08:35:33 +0000 |
| commit | 451fd6cc90aaa14816376a0285334678df9ce484 (patch) | |
| tree | 034e18752050b9e92a7cdcdf3e46c1ceed8c0cc6 /addons/l10n_in/models | |
| parent | 8c0d62082f6eb71ec106126db2f7f046a9b8b1b5 (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__.py | 5 | ||||
| -rw-r--r-- | addons/l10n_in/models/account.py | 40 | ||||
| -rw-r--r-- | addons/l10n_in/models/account_invoice.py | 62 | ||||
| -rw-r--r-- | addons/l10n_in/models/chart_template.py | 32 | ||||
| -rw-r--r-- | addons/l10n_in/models/port_code.py | 19 | ||||
| -rw-r--r-- | addons/l10n_in/models/product_template.py | 1 | ||||
| -rw-r--r-- | addons/l10n_in/models/res_config_settings.py | 15 | ||||
| -rw-r--r-- | addons/l10n_in/models/res_partner.py | 17 | ||||
| -rw-r--r-- | addons/l10n_in/models/uom_uom.py | 11 |
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") |
