mirror of
https://github.com/bringout/oca-ocb-l10n_asia-pacific.git
synced 2026-04-27 04:02:01 +02:00
19.0 vanilla
This commit is contained in:
parent
7dc55599c6
commit
7f43bbbfcc
650 changed files with 45260 additions and 33436 deletions
|
|
@ -1,9 +1,17 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
import logging
|
||||
import re
|
||||
from stdnum.in_ import pan
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.exceptions import UserError, AccessError, ValidationError
|
||||
from odoo.addons.l10n_in.models.iap_account import IAP_SERVICE_NAME
|
||||
from odoo.tools.misc import clean_context
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
TEST_GST_NUMBER = "36AABCT1332L011"
|
||||
TEST_GST_NUMBER_BVM = "29AAGCB1286Q000"
|
||||
|
||||
|
||||
class ResPartner(models.Model):
|
||||
_inherit = 'res.partner'
|
||||
|
|
@ -19,33 +27,206 @@ class ResPartner(models.Model):
|
|||
('uin_holders', 'UIN Holders'),
|
||||
], string="GST Treatment")
|
||||
|
||||
@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'
|
||||
l10n_in_pan_entity_id = fields.Many2one(
|
||||
comodel_name='l10n_in.pan.entity',
|
||||
string="PAN",
|
||||
ondelete='restrict',
|
||||
help="PAN enables the department to link all transactions of the person with the department.\n"
|
||||
"These transactions include taxpayments, TDS/TCS credits, returns of income/wealth/gift/FBT,"
|
||||
" specified transactions, correspondence, and so on.\n"
|
||||
"Thus, PAN acts as an identifier for the person with the tax department."
|
||||
)
|
||||
l10n_in_tan = fields.Char("TAN")
|
||||
|
||||
display_pan_warning = fields.Boolean(string="Display pan warning", compute="_compute_display_pan_warning")
|
||||
l10n_in_gst_state_warning = fields.Char(compute="_compute_l10n_in_gst_state_warning")
|
||||
l10n_in_is_gst_registered_enabled = fields.Boolean(compute="_compute_l10n_in_gst_registered_and_status")
|
||||
|
||||
# gstin_status related field
|
||||
l10n_in_gstin_verified_status = fields.Boolean(string="GST Status", tracking=True)
|
||||
l10n_in_gstin_verified_date = fields.Date(string="GSTIN Verified Date", tracking=True)
|
||||
l10n_in_gstin_status_feature_enabled = fields.Boolean(compute="_compute_l10n_in_gst_registered_and_status")
|
||||
|
||||
@api.depends('vat', 'state_id', 'country_id', 'fiscal_country_codes')
|
||||
def _compute_l10n_in_gst_state_warning(self):
|
||||
for partner in self:
|
||||
if (
|
||||
"IN" in partner.fiscal_country_codes
|
||||
and partner.check_vat_in(partner.vat)
|
||||
):
|
||||
if partner.vat[:2] == "99":
|
||||
partner.l10n_in_gst_state_warning = _(
|
||||
"As per GSTN the country should be other than India, so it's recommended to"
|
||||
)
|
||||
else:
|
||||
state_id = self.env['res.country.state'].search([('l10n_in_tin', '=', partner.vat[:2])], limit=1)
|
||||
if state_id and state_id != partner.state_id:
|
||||
partner.l10n_in_gst_state_warning = _(
|
||||
"As per GSTN the state should be %s, so it's recommended to", state_id.name
|
||||
)
|
||||
else:
|
||||
partner.l10n_in_gst_state_warning = False
|
||||
else:
|
||||
partner.l10n_in_gst_state_warning = False
|
||||
|
||||
@api.depends('l10n_in_pan_entity_id')
|
||||
def _compute_display_pan_warning(self):
|
||||
for partner in self:
|
||||
partner.display_pan_warning = partner.vat and partner.l10n_in_pan_entity_id and partner.l10n_in_pan_entity_id.name != partner.vat[2:12]
|
||||
|
||||
@api.depends('company_id.l10n_in_is_gst_registered', 'company_id.l10n_in_gstin_status_feature')
|
||||
def _compute_l10n_in_gst_registered_and_status(self):
|
||||
for record in self:
|
||||
company = record.company_id or self.env.company
|
||||
record.l10n_in_is_gst_registered_enabled = company.l10n_in_is_gst_registered
|
||||
record.l10n_in_gstin_status_feature_enabled = company.l10n_in_gstin_status_feature
|
||||
|
||||
@api.onchange('vat')
|
||||
def _onchange_l10n_in_gst_status(self):
|
||||
"""
|
||||
Reset GST Status Whenever the `vat` of partner changes
|
||||
"""
|
||||
for partner in self:
|
||||
if partner.country_code == 'IN' and (partner.l10n_in_gstin_verified_status or partner.l10n_in_gstin_verified_date):
|
||||
partner.l10n_in_gstin_verified_status = False
|
||||
partner.l10n_in_gstin_verified_date = False
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
res = super().create(vals_list)
|
||||
if 'import_file' in self.env.context:
|
||||
return res
|
||||
for partner in res.filtered(lambda p: p.country_code == 'IN' and p.vat and p.check_vat_in(p.vat)):
|
||||
partner._set_l10n_in_pan_tan_from_vat()
|
||||
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'
|
||||
def write(self, vals):
|
||||
res = super().write(vals)
|
||||
if 'import_file' in self.env.context:
|
||||
return res
|
||||
if vals.get('vat') or vals.get('country_id'):
|
||||
for partner in self.filtered(lambda p: p.country_code == 'IN' and p.vat and p.check_vat_in(p.vat)):
|
||||
partner._set_l10n_in_pan_tan_from_vat()
|
||||
return res
|
||||
|
||||
def _set_l10n_in_pan_tan_from_vat(self):
|
||||
self.ensure_one()
|
||||
identifier = self.vat[2:12].upper()
|
||||
if pan.is_valid(identifier):
|
||||
self.l10n_in_pan_entity_id = self._l10n_in_search_create_pan_entity_from_vat(self.vat).id
|
||||
elif re.match(r'^[A-Z]{4}[0-9]{5}[A-Z]{1}$', identifier):
|
||||
self.l10n_in_tan = identifier
|
||||
|
||||
def _l10n_in_search_create_pan_entity_from_vat(self, vat):
|
||||
pan_number = vat[2:12].upper()
|
||||
pan_entity = self.env['l10n_in.pan.entity'].search([('name', '=', pan_number)], limit=1)
|
||||
if not pan_entity:
|
||||
context = clean_context(self.env.context)
|
||||
pan_entity = self.env['l10n_in.pan.entity'].with_context(context).create({'name': pan_number})
|
||||
return pan_entity
|
||||
|
||||
def action_l10n_in_verify_gstin_status(self):
|
||||
self.ensure_one()
|
||||
self.check_access('write')
|
||||
if self.env.company.sudo().account_fiscal_country_id.code != 'IN':
|
||||
raise UserError(_('You must be logged in an Indian company to use this feature'))
|
||||
if not self.vat:
|
||||
raise ValidationError(_("Please enter the GSTIN"))
|
||||
if not self.env.company.l10n_in_gstin_status_feature:
|
||||
raise ValidationError(_("This feature is not activated. Go to Settings to activate this feature."))
|
||||
is_production = self.env.company.sudo().l10n_in_edi_production_env
|
||||
params = {
|
||||
"gstin_to_search": self.vat,
|
||||
"gstin": self.env.company.vat,
|
||||
}
|
||||
try:
|
||||
response = self.env['iap.account']._l10n_in_connect_to_server(
|
||||
is_production,
|
||||
params,
|
||||
'/iap/l10n_in_reports/1/public/search',
|
||||
"l10n_in.endpoint"
|
||||
)
|
||||
except AccessError:
|
||||
raise UserError(_("Unable to connect with GST network"))
|
||||
if response.get('error') and any(e.get('code') == 'no-credit' for e in response['error']):
|
||||
return self.env["bus.bus"]._sendone(self.env.user.partner_id, "iap_notification",
|
||||
{
|
||||
"type": "no_credit",
|
||||
"title": _("Not enough credits to check GSTIN status"),
|
||||
"get_credits_url": self.env["iap.account"].get_credits_url(service_name=IAP_SERVICE_NAME),
|
||||
},
|
||||
)
|
||||
gst_status = response.get('data', {}).get('sts', "")
|
||||
if gst_status.casefold() == 'active':
|
||||
l10n_in_gstin_verified_status = True
|
||||
elif gst_status:
|
||||
l10n_in_gstin_verified_status = False
|
||||
date_from = response.get("data", {}).get("cxdt", '')
|
||||
if date_from and re.search(r'\d', date_from):
|
||||
message = _(
|
||||
"GSTIN %(vat)s is %(status)s and Effective from %(date_from)s.",
|
||||
vat=self.vat,
|
||||
status=gst_status,
|
||||
date_from=date_from,
|
||||
)
|
||||
else:
|
||||
message = _(
|
||||
"GSTIN %(vat)s is %(status)s, effective date is not available.",
|
||||
vat=self.vat,
|
||||
status=gst_status
|
||||
)
|
||||
if not is_production:
|
||||
message += _(" Warning: You are currently in a test environment. The result is a dummy.")
|
||||
self.message_post(body=message)
|
||||
else:
|
||||
_logger.info("GST status check error %s", response)
|
||||
if response.get('error') and any(e.get('code') == 'SWEB_9035' for e in response['error']):
|
||||
raise UserError(
|
||||
_("The provided GSTIN is invalid. Please check the GSTIN and try again.")
|
||||
)
|
||||
default_error_message = _(
|
||||
"Something went wrong while fetching the GST status."
|
||||
"Please Contact Support if the error persists with"
|
||||
"Response: %(response)s",
|
||||
response=response
|
||||
)
|
||||
error_messages = [
|
||||
f"[{error.get('code') or _('Unknown')}] {error.get('message') or default_error_message}"
|
||||
for error in response.get('error')
|
||||
]
|
||||
raise UserError(
|
||||
error_messages
|
||||
and '\n'.join(error_messages)
|
||||
or default_error_message
|
||||
)
|
||||
self.write({
|
||||
"l10n_in_gstin_verified_status": l10n_in_gstin_verified_status,
|
||||
"l10n_in_gstin_verified_date": fields.Date.today(),
|
||||
})
|
||||
return {
|
||||
"type": "ir.actions.client",
|
||||
"tag": "display_notification",
|
||||
"params": {
|
||||
"type": "info",
|
||||
"message": _("GSTIN Status Updated Successfully"),
|
||||
"next": {"type": "ir.actions.act_window_close"},
|
||||
},
|
||||
}
|
||||
|
||||
@api.onchange('vat')
|
||||
def onchange_vat(self):
|
||||
if self.vat and self.check_vat_in(self.vat):
|
||||
self.vat = self.vat.upper()
|
||||
state_id = self.env['res.country.state'].search([('l10n_in_tin', '=', self.vat[:2])], limit=1)
|
||||
if state_id:
|
||||
self.state_id = state_id
|
||||
pan_entity = self.env['l10n_in.pan.entity'].search([('name', '=', self.vat[2:12])], limit=1)
|
||||
if pan_entity:
|
||||
self.l10n_in_pan_entity_id = pan_entity.id
|
||||
|
||||
@api.model
|
||||
def _commercial_fields(self):
|
||||
res = super()._commercial_fields()
|
||||
return res + ['l10n_in_gst_treatment']
|
||||
return super()._commercial_fields() + ['l10n_in_gst_treatment', 'l10n_in_pan_entity_id', 'l10n_in_tan']
|
||||
|
||||
def check_vat_in(self, vat):
|
||||
"""
|
||||
|
|
@ -53,6 +234,28 @@ class ResPartner(models.Model):
|
|||
but this is not a valid number as per the regular expression
|
||||
so TEST_GST_NUMBER is considered always valid
|
||||
"""
|
||||
if vat == TEST_GST_NUMBER:
|
||||
if vat in (TEST_GST_NUMBER, TEST_GST_NUMBER_BVM):
|
||||
return True
|
||||
return super().check_vat_in(vat)
|
||||
|
||||
@api.model
|
||||
def _l10n_in_get_partner_vals_by_vat(self, vat):
|
||||
partner_data = self.enrich_by_gst(vat)
|
||||
for fname in list(partner_data.keys()):
|
||||
if fname not in self.env['res.partner']._fields:
|
||||
partner_data.pop(fname, None)
|
||||
partner_data.update({
|
||||
'country_id': partner_data.get('country_id', {}).get('id'),
|
||||
'state_id': partner_data.get('state_id', {}).get('id'),
|
||||
'company_type': 'company',
|
||||
'l10n_in_gst_treatment': partner_data.get('l10n_in_gst_treatment', 'regular'),
|
||||
})
|
||||
return partner_data
|
||||
|
||||
def action_update_state_as_per_gstin(self):
|
||||
self.ensure_one()
|
||||
if self.check_vat_in(self.vat):
|
||||
state_id = self.env['res.country.state'].search([('l10n_in_tin', '=', self.vat[:2])], limit=1)
|
||||
self.state_id = state_id
|
||||
if self.ref_company_ids:
|
||||
self.ref_company_ids._update_l10n_in_fiscal_position()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue