diff --git a/addons/l10n_in/__manifest__.py b/addons/l10n_in/__manifest__.py
index cd12e728b5e..324703939fa 100644
--- a/addons/l10n_in/__manifest__.py
+++ b/addons/l10n_in/__manifest__.py
@@ -18,7 +18,7 @@ Sheet, now only Vertical format has been permitted Which is Supported By Odoo.
""",
'category': 'Accounting/Localizations',
'depends': [
- 'account_tax_python',
+ 'account_tax_python', 'base_vat',
],
'data': [
'security/l10n_in_security.xml',
diff --git a/addons/l10n_in/models/account.py b/addons/l10n_in/models/account.py
index eb10eced9b9..bdc6e36d0a3 100644
--- a/addons/l10n_in/models/account.py
+++ b/addons/l10n_in/models/account.py
@@ -9,8 +9,7 @@ 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.")
- l10n_in_gstin_partner_id = fields.Many2one('res.partner', string="GSTIN", ondelete="restrict", help="GSTIN related to this journal. If empty then consider as company GSTIN.")
+ l10n_in_gstin_partner_id = fields.Many2one('res.partner', string="GSTIN Unit", ondelete="restrict", help="GSTIN related to this journal. If empty then consider as company GSTIN.")
def name_get(self):
"""
diff --git a/addons/l10n_in/models/account_invoice.py b/addons/l10n_in/models/account_invoice.py
index 9a8a8496803..5f7b203d0f3 100644
--- a/addons/l10n_in/models/account_invoice.py
+++ b/addons/l10n_in/models/account_invoice.py
@@ -2,6 +2,7 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, fields, models, _
+from odoo.exceptions import ValidationError
class AccountMove(models.Model):
@@ -13,21 +14,43 @@ class AccountMove(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)
+ l10n_in_gst_treatment = fields.Selection([
+ ('regular', 'Registered Business - Regular'),
+ ('composition', 'Registered Business - Composition'),
+ ('unregistered', 'Unregistered Business'),
+ ('consumer', 'Consumer'),
+ ('overseas', 'Overseas'),
+ ('special_economic_zone', 'Special Economic Zone'),
+ ('deemed_export', 'Deemed Export')
+ ], string="GST Treatment", readonly=True, states={'draft': [('readonly', False)]})
+ l10n_in_state_id = fields.Many2one('res.country.state', string="Location of supply")
+ l10n_in_company_country_code = fields.Char(related='company_id.country_id.code', string="Country code")
+ l10n_in_gstin = fields.Char(string="GSTIN")
# 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_shipping_port_code_id = fields.Many2one('l10n_in.port.code', '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)
+
+ @api.onchange('partner_id')
+ def _onchange_partner_id(self):
+ """Use journal type to define document type because not miss state in any entry including POS entry"""
+ if self.l10n_in_company_country_code == 'IN':
+ self.l10n_in_gst_treatment = self.partner_id.l10n_in_gst_treatment
+ return super()._onchange_partner_id()
+
+ @api.model
+ def _l10n_in_get_indian_state(self, partner):
+ """In tax return filing, If customer is not Indian in that case place of supply is must set to Other Territory.
+ So we set Other Territory in l10n_in_state_id when customer(partner) is not Indian
+ Also we raise if state is not set in Indian customer.
+ State is big role under GST because tax type is depend on.for more information check this https://www.cbic.gov.in/resources//htdocs-cbec/gst/Integrated%20goods%20&%20Services.pdf"""
+ if partner.country_id and partner.country_id.code == 'IN' and not partner.state_id:
+ raise ValidationError(_("State is missing from address in '%s'. First set state after post this invoice again." %(partner.name)))
+ elif partner.country_id and partner.country_id.code != 'IN':
+ return self.env.ref('l10n_in.state_in_ot')
+ return partner.state_id
+
@api.model
def _get_tax_grouping_key_from_tax_line(self, tax_line):
@@ -53,3 +76,39 @@ class AccountMove(models.Model):
line.product_id.id,
]
return tax_key
+
+ def _l10n_in_get_shipping_partner(self):
+ """Overwrite in sale"""
+ self.ensure_one()
+ return self.partner_id
+
+ @api.model
+ def _l10n_in_get_shipping_partner_gstin(self, shipping_partner):
+ """Overwrite in sale"""
+ return shipping_partner.vat
+
+ def post(self):
+ """Use journal type to define document type because not miss state in any entry including POS entry"""
+ res = super().post()
+ gst_treatment_name_mapping = {k: v for k, v in
+ self._fields['l10n_in_gst_treatment']._description_selection(self.env)}
+ for move in self.filtered(lambda m: m.l10n_in_company_country_code == 'IN'):
+ """Check state is set in company/sub-unit"""
+ company_unit_partner = move.journal_id.l10n_in_gstin_partner_id or move.journal_id.company_id
+ if not company_unit_partner.state_id:
+ raise ValidationError(_("State is missing from your company/unit %s(%s).\nFirst set state in your company/unit." % (company_unit_partner.name, company_unit_partner.id)))
+ elif self.journal_id.type == 'purchase':
+ move.l10n_in_state_id = company_unit_partner.state_id
+
+ shipping_partner = move._l10n_in_get_shipping_partner()
+ move.l10n_in_gstin = move._l10n_in_get_shipping_partner_gstin(shipping_partner)
+ if not move.l10n_in_gstin and move.l10n_in_gst_treatment in ['regular', 'composition', 'special_economic_zone', 'deemed_export']:
+ raise ValidationError(_("Partner %s(%s) GSTIN is required under GST Treatment %s" % (shipping_partner.name, shipping_partner.id, gst_treatment_name_mapping.get(move.l10n_in_gst_treatment))))
+ if self.journal_id.type == 'sale':
+ move.l10n_in_state_id = self._l10n_in_get_indian_state(shipping_partner)
+ if not move.l10n_in_state_id:
+ move.l10n_in_state_id = self._l10n_in_get_indian_state(move.partner_id)
+ #still state is not set then assumed that transaction is local like PoS so set state of company unit
+ if not move.l10n_in_state_id:
+ move.l10n_in_state_id = company_unit_partner.state_id
+ return res
diff --git a/addons/l10n_in/models/chart_template.py b/addons/l10n_in/models/chart_template.py
index 39f3270b755..cb18bb758a1 100644
--- a/addons/l10n_in/models/chart_template.py
+++ b/addons/l10n_in/models/chart_template.py
@@ -15,11 +15,6 @@ class AccountChartTemplate(models.Model):
journal['l10n_in_gstin_partner_id'] = company.partner_id.id
if journal['code'] == 'INV':
journal['name'] = _('Tax Invoices')
-
- res += [
- {'type': 'sale', 'name': _('Retail Invoices'), 'code': 'RETINV', 'company_id': company.id, 'show_on_dashboard': True, 'l10n_in_gstin_partner_id': company.partner_id.id},
- {'type': 'sale', 'name': _('Export Invoices'), 'code': 'EXPINV', 'company_id': company.id, 'show_on_dashboard': True, 'l10n_in_import_export': True, 'l10n_in_gstin_partner_id': company.partner_id.id}
- ]
return res
class AccountTaxTemplate(models.Model):
diff --git a/addons/l10n_in/models/res_partner.py b/addons/l10n_in/models/res_partner.py
index d1c5a27d9a9..f14e39536a3 100644
--- a/addons/l10n_in/models/res_partner.py
+++ b/addons/l10n_in/models/res_partner.py
@@ -2,16 +2,45 @@
# 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")
+ l10n_in_gst_treatment = fields.Selection([
+ ('regular', 'Registered Business - Regular'),
+ ('composition', 'Registered Business - Composition'),
+ ('unregistered', 'Unregistered Business'),
+ ('consumer', 'Consumer'),
+ ('overseas', 'Overseas'),
+ ('special_economic_zone', 'Special Economic Zone'),
+ ('deemed_export', 'Deemed Export'),
+ ], string="GST Treatment")
- @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))
+ @api.onchange('company_type')
+ def onchange_company_type(self):
+ res = super().onchange_company_type()
+ if self.country_id and self.country_id.code == 'IN':
+ self.l10n_in_gst_treatment = (self.company_type == 'company') and 'regular' or 'consumer'
+ return res
+
+ @api.onchange('country_id')
+ def _onchange_country_id(self):
+ res = super()._onchange_country_id()
+ if self.country_id and self.country_id.code != 'IN':
+ self.l10n_in_gst_treatment = 'overseas'
+ elif self.country_id and self.country_id.code == 'IN':
+ self.l10n_in_gst_treatment = (self.company_type == 'company') and 'regular' or 'consumer'
+ return res
+
+ @api.onchange('vat')
+ def onchange_vat(self):
+ if self.vat and self.check_vat_in(self.vat):
+ state_id = self.env['res.country.state'].search([('l10n_in_tin', '=', self.vat[:2])], limit=1)
+ if state_id:
+ self.state_id = state_id
+
+ @api.model
+ def _commercial_fields(self):
+ res = super()._commercial_fields()
+ return res + ['l10n_in_gst_treatment']
diff --git a/addons/l10n_in/report/account_invoice_report.py b/addons/l10n_in/report/account_invoice_report.py
index 5853d298343..5f59a4497aa 100644
--- a/addons/l10n_in/report/account_invoice_report.py
+++ b/addons/l10n_in/report/account_invoice_report.py
@@ -16,12 +16,15 @@ class L10nInAccountInvoiceReport(models.Model):
name = fields.Char(string="Invoice Number")
partner_id = fields.Many2one('res.partner', string="Customer")
is_reverse_charge = fields.Char("Reverse Charge")
- 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')])
+ l10n_in_gst_treatment = fields.Selection([
+ ('regular', 'Registered Business - Regular'),
+ ('composition', 'Registered Business - Composition'),
+ ('unregistered', 'Unregistered Business'),
+ ('consumer', 'Consumer'),
+ ('overseas', 'Overseas'),
+ ('special_economic_zone', 'Special Economic Zone'),
+ ('deemed_export', 'Deemed Export'),
+ ], string="GST Treatment")
journal_id = fields.Many2one('account.journal', string="Journal")
state = fields.Selection([('draft', 'Unposted'), ('posted', 'Posted')], string='Status')
igst_amount = fields.Float(string="IGST Amount")
@@ -70,7 +73,7 @@ class L10nInAccountInvoiceReport(models.Model):
sub.state,
sub.partner_id,
sub.date,
- sub.l10n_in_export_type,
+ sub.l10n_in_gst_treatment,
sub.ecommerce_partner_id,
sub.shipping_bill_number,
sub.shipping_bill_date,
@@ -118,7 +121,7 @@ class L10nInAccountInvoiceReport(models.Model):
am.name,
am.state,
am.date,
- am.l10n_in_export_type AS l10n_in_export_type,
+ am.l10n_in_gst_treatment AS l10n_in_gst_treatment,
am.l10n_in_reseller_partner_id AS ecommerce_partner_id,
am.l10n_in_shipping_bill_number AS shipping_bill_number,
am.l10n_in_shipping_bill_date AS shipping_bill_date,
@@ -128,7 +131,7 @@ class L10nInAccountInvoiceReport(models.Model):
aj.company_id,
am.move_type AS move_type,
am.reversed_entry_id AS reversed_entry_id,
- p.vat AS partner_vat,
+ am.l10n_in_gstin AS partner_vat,
CASE WHEN rp.vat IS NULL THEN '' ELSE rp.vat END AS ecommerce_vat,
(CASE WHEN at.l10n_in_reverse_charge = True
THEN True
@@ -157,34 +160,32 @@ class L10nInAccountInvoiceReport(models.Model):
THEN 'E'
ELSE 'OE'
END) as b2cs_is_ecommerce,
- (CASE WHEN ps.id = cp.state_id or p.id IS NULL
+ (CASE WHEN am.l10n_in_state_id = cp.state_id or p.id IS NULL
THEN 'Intra State'
- WHEN ps.id != cp.state_id and p.id IS NOT NULL
+ WHEN am.l10n_in_state_id != cp.state_id and p.id IS NOT NULL
THEN 'Inter State'
END) AS supply_type,
- (CASE WHEN am.l10n_in_export_type in ('deemed', 'export_with_igst', 'sez_with_igst')
+ (CASE WHEN am.l10n_in_gst_treatment in ('deemed_export', 'overseas') and am.amount_tax > 0.00
THEN 'EXPWP'
- WHEN am.l10n_in_export_type in ('sale_from_bonded_wh', 'sez_without_igst')
+ WHEN am.l10n_in_gst_treatment in ('deemed_export', 'overseas') and am.amount_tax <= 0.00
THEN 'EXPWOP'
ELSE ''
END) AS export_type,
- (CASE WHEN refund_am.l10n_in_export_type in ('deemed', 'export_with_igst', 'sez_with_igst')
+ (CASE WHEN am.l10n_in_gst_treatment in ('deemed_export', 'overseas') and am.amount_tax > 0.00
THEN 'EXPWP'
- WHEN refund_am.l10n_in_export_type in ('sale_from_bonded_wh', 'sez_without_igst')
+ WHEN am.l10n_in_gst_treatment in ('deemed_export', 'overseas') and am.amount_tax <= 0.00
THEN 'EXPWOP'
ELSE 'B2CL'
END) AS refund_export_type,
- (CASE WHEN am.l10n_in_export_type = 'regular'
+ (CASE WHEN am.l10n_in_gst_treatment = 'regular'
THEN 'Regular'
- WHEN am.l10n_in_export_type = 'deemed'
+ WHEN am.l10n_in_gst_treatment = 'deemed_export'
THEN 'Deemed'
- WHEN am.l10n_in_export_type = 'sale_from_bonded_wh'
- THEN 'Sale from Bonded WH'
- WHEN am.l10n_in_export_type = 'export_with_igst'
+ WHEN am.l10n_in_gst_treatment = 'overseas' and am.amount_tax > 0.00
THEN 'Export with IGST'
- WHEN am.l10n_in_export_type = 'sez_with_igst'
+ WHEN am.l10n_in_gst_treatment = 'special_economic_zone' and am.amount_tax > 0.00
THEN 'SEZ with IGST payment'
- WHEN am.l10n_in_export_type = 'sez_without_igst'
+ WHEN am.l10n_in_gst_treatment = 'special_economic_zone' and am.amount_tax <= 0.00
THEN 'SEZ without IGST payment'
END) AS b2b_type,
(CASE WHEN am.move_type = 'out_refund'
@@ -250,11 +251,11 @@ class L10nInAccountInvoiceReport(models.Model):
JOIN account_account_tag_account_move_line_rel aat_aml_rel ON aat_aml_rel.account_move_line_id = aml.id
JOIN account_account_tag aat ON aat.id = aat_aml_rel.account_account_tag_id
JOIN account_tax_report_line_tags_rel tag_rep_ln ON aat.id = tag_rep_ln.account_account_tag_id
- LEFT JOIN res_partner cp ON cp.id = c.partner_id
+ LEFT JOIN res_partner cp ON cp.id = COALESCE(aj.l10n_in_gstin_partner_id, c.partner_id)
LEFT JOIN res_country_state cps ON cps.id = cp.state_id
LEFT JOIN account_move refund_am ON refund_am.id = am.reversed_entry_id
LEFT JOIN res_partner p ON p.id = aml.partner_id
- LEFT JOIN res_country_state ps ON ps.id = p.state_id
+ LEFT JOIN res_country_state ps ON ps.id = am.l10n_in_state_id
LEFT JOIN res_partner rp ON rp.id = am.l10n_in_reseller_partner_id
LEFT JOIN account_tax_filiation_rel atr ON atr.child_tax = at.id
LEFT JOIN account_tax parent_at ON parent_at.id = atr.parent_tax
@@ -275,7 +276,7 @@ class L10nInAccountInvoiceReport(models.Model):
sub.state,
sub.partner_id,
sub.date,
- sub.l10n_in_export_type,
+ sub.l10n_in_gst_treatment,
sub.ecommerce_partner_id,
sub.shipping_bill_number,
sub.shipping_bill_date,
diff --git a/addons/l10n_in/report/account_payment_report.py b/addons/l10n_in/report/account_payment_report.py
index d9ae35bbde6..db02965b18d 100644
--- a/addons/l10n_in/report/account_payment_report.py
+++ b/addons/l10n_in/report/account_payment_report.py
@@ -92,7 +92,7 @@ class L10nInPaymentReport(models.AbstractModel):
ELSE c.account_purchase_tax_id END)
JOIN res_partner p ON p.id = aml.partner_id
LEFT JOIN res_country_state ps ON ps.id = p.state_id
- LEFT JOIN res_partner cp ON cp.id = c.partner_id
+ LEFT JOIN res_partner cp ON cp.id = COALESCE(aj.l10n_in_gstin_partner_id, c.partner_id)
LEFT JOIN res_country_state cps ON cps.id = cp.state_id
"""
diff --git a/addons/l10n_in/report/exempted_gst_report.py b/addons/l10n_in/report/exempted_gst_report.py
index 4ea2666935f..661a5c08b9a 100644
--- a/addons/l10n_in/report/exempted_gst_report.py
+++ b/addons/l10n_in/report/exempted_gst_report.py
@@ -82,7 +82,7 @@ class L10nInExemptedReport(models.Model):
JOIN account_account aa ON aa.id = aml.account_id
JOIN account_journal aj ON aj.id = am.journal_id
JOIN res_company c ON c.id = aj.company_id
- LEFT JOIN res_partner cp ON cp.id = c.partner_id
+ LEFT JOIN res_partner cp ON cp.id = COALESCE(aj.l10n_in_gstin_partner_id, c.partner_id)
LEFT JOIN res_partner p ON p.id = am.partner_id
LEFT JOIN res_country pc ON pc.id = p.country_id
WHERE aa.internal_type = 'other' and aml.tax_line_id IS NULL
diff --git a/addons/l10n_in/views/account_invoice_views.xml b/addons/l10n_in/views/account_invoice_views.xml
index 5b716a78688..f2f55f341e9 100644
--- a/addons/l10n_in/views/account_invoice_views.xml
+++ b/addons/l10n_in/views/account_invoice_views.xml
@@ -5,23 +5,27 @@
account.move
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/addons/l10n_in/views/account_journal_views.xml b/addons/l10n_in/views/account_journal_views.xml
index 01d68c9733e..626863ae7fb 100644
--- a/addons/l10n_in/views/account_journal_views.xml
+++ b/addons/l10n_in/views/account_journal_views.xml
@@ -8,9 +8,6 @@
{'invisible': [('type', 'not in', ['bank', 'cash', 'sale', 'purchase'])]}
-
-
-
diff --git a/addons/l10n_in/views/report_invoice.xml b/addons/l10n_in/views/report_invoice.xml
index 15aa1e270dd..f77aa0134df 100644
--- a/addons/l10n_in/views/report_invoice.xml
+++ b/addons/l10n_in/views/report_invoice.xml
@@ -2,7 +2,22 @@
-
+
+ o.company_id.country_id.code != 'IN'
+
+
+
+
+
+
+
+ Destination of supply:
+
+
+ Place of supply:
+
+
+
@@ -12,9 +27,9 @@
-
+
- HSN/SAC |
+ HSN/SAC |
diff --git a/addons/l10n_in/views/res_partner_views.xml b/addons/l10n_in/views/res_partner_views.xml
index e3b3488bd29..98e112c01e2 100644
--- a/addons/l10n_in/views/res_partner_views.xml
+++ b/addons/l10n_in/views/res_partner_views.xml
@@ -7,13 +7,10 @@
- GSTIN
+ {'required':[('l10n_in_gst_treatment', 'in', ['regular', 'composition', 'special_economic_zone', 'deemed_export'])], 'readonly': [('parent_id', '!=', False)]}
-
-
-
-
- {'required': ['|', ('l10n_in_country_code','=', 'IN'), ('l10n_in_country_code','=', False)]}
+
+