Skip to content

Commit

Permalink
[REF] l10n_in*: add iap to l10n_in and add l10n_in_gstin_status module
Browse files Browse the repository at this point in the history
This commit introduces two things:
1. a small refactor for `l10n_in*`:
Before this commit l10n_in_edi used to be depends on `iap` but
now we have moved the dependency of `iap` to `l10n_in` because
part 2 introduces a new module using `iap`. Hence it's best
to change `iap` dependency and reduces the code duplication

2. A new module enabling users to check the GSTIN status of
vendors. We can install it manually or with 'GST Status API Service' in the
'Customer Invoices' section in the Accounting settings.

After installation, Users will find a 'Check Status'  button on the partner
form. By clicking, the system retrieves the latest GSTIN status from the service
provider, displaying the GSTIN status, and the date of the last verification.
Now the 'Reverify' button is shown on partner form, vendor bills, customer
invoices, credit notes, and debit notes to reverify the status. This enhancement
ensures users have up-to-date information regarding vendor compliance directly
within Odoo.

task-3707122

closes odoo#175290

Related: odoo/enterprise#67787
Related: odoo/upgrade#6335
Signed-off-by: Josse Colpaert (jco) <[email protected]>
  • Loading branch information
hamo-odoo committed Aug 12, 2024
1 parent 570386d commit 2952b90
Show file tree
Hide file tree
Showing 23 changed files with 454 additions and 104 deletions.
1 change: 1 addition & 0 deletions addons/l10n_in/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
'base_vat',
'account_debit_note',
'account',
'iap',
],
'auto_install': ['account'],
'data': [
Expand Down
1 change: 1 addition & 0 deletions addons/l10n_in/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from . import account_move_line
from . import account_tax
from . import company
from . import iap_account
from . import product_template
from . import port_code
from . import res_config_settings
Expand Down
5 changes: 5 additions & 0 deletions addons/l10n_in/models/company.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ class ResCompany(models.Model):
store=True,
readonly=False,
)
l10n_in_edi_production_env = fields.Boolean(
string="Indian Production Environment",
help="Enable the use of production credentials",
groups="base.group_system",
)

@api.depends('vat')
def _compute_l10n_in_hsn_code_digit(self):
Expand Down
27 changes: 27 additions & 0 deletions addons/l10n_in/models/iap_account.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.

from odoo import api, models
from odoo.addons.iap import jsonrpc

DEFAULT_IAP_ENDPOINT = "https://l10n-in-edi.api.odoo.com"
DEFAULT_IAP_TEST_ENDPOINT = "https://l10n-in-edi-demo.api.odoo.com"
IAP_SERVICE_NAME = 'l10n_in_edi'


class IapAccount(models.Model):
_inherit = 'iap.account'

@api.model
def _l10n_in_connect_to_server(self, is_production, params, url_path, config_parameter, timeout=25):
user_token = self.get(IAP_SERVICE_NAME)
params.update({
"dbuuid": self.env["ir.config_parameter"].sudo().get_param("database.uuid"),
"account_token": user_token.account_token,
})
if is_production:
default_endpoint = DEFAULT_IAP_ENDPOINT
else:
default_endpoint = DEFAULT_IAP_TEST_ENDPOINT
endpoint = self.env["ir.config_parameter"].sudo().get_param(config_parameter, default_endpoint)
url = "%s%s" % (endpoint, url_path)
return jsonrpc(url, params=params, timeout=timeout)
22 changes: 21 additions & 1 deletion addons/l10n_in/models/res_config_settings.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,33 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.

from odoo import api, fields, models
from odoo import _, fields, models
from odoo.exceptions import ValidationError
from odoo.addons.l10n_in.models.iap_account import IAP_SERVICE_NAME


class ResConfigSettings(models.TransientModel):
_inherit = 'res.config.settings'

group_l10n_in_reseller = fields.Boolean(implied_group='l10n_in.group_l10n_in_reseller', string="Manage Reseller(E-Commerce)")
l10n_in_edi_production_env = fields.Boolean(
string="Indian Production Environment",
related="company_id.l10n_in_edi_production_env",
readonly=False
)
module_l10n_in_edi = fields.Boolean('Indian Electronic Invoicing')
module_l10n_in_edi_ewaybill = fields.Boolean('Indian Electronic Waybill')
module_l10n_in_gstin_status = fields.Boolean('Check GST Number Status')
l10n_in_hsn_code_digit = fields.Selection(related='company_id.l10n_in_hsn_code_digit', readonly=False)

def l10n_in_edi_buy_iap(self):
if not self.l10n_in_edi_production_env or not (self.module_l10n_in_edi or self.module_l10n_in_gstin_status):
raise ValidationError(_(
"Please ensure that at least one Indian service and production environment is enabled,"
" and save the configuration to proceed with purchasing credits."
))
return {
'type': 'ir.actions.act_url',
'url': self.env["iap.account"].get_credits_url(service_name=IAP_SERVICE_NAME),
'target': '_new'
}
45 changes: 38 additions & 7 deletions addons/l10n_in/views/res_config_settings_views.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,48 @@
<setting help="Use this if setup with Reseller(E-Commerce)." name="ecommerce_reseller_setting" title="Manage Reseller(E-Commerce)" invisible="country_code != 'IN'">
<field name="group_l10n_in_reseller"/>
</setting>
<setting help="Connect to NIC (National Informatics Center) to submit invoices on posting." name="electronic_invoices_in" invisible="country_code != 'IN'" documentation="/applications/finance/accounting/fiscal_localizations/localizations/india.html#indian-e-invoicing">
<field name="module_l10n_in_edi" class="oe_inline" widget="upgrade_boolean"/>
</setting>
<setting help="Connect to NIC (National Informatics Center) to submit e-waybill on posting." name="electronic_waybill_in" invisible="country_code != 'IN'" documentation="/applications/finance/accounting/fiscal_localizations/localizations/india.html#indian-e-waybill">
<field name="module_l10n_in_edi_ewaybill" class="oe_inline" widget="upgrade_boolean"/>
</setting>
</block>
<xpath expr="//app[@name='account']" position="inside">
<div id="india_integration_section" invisible="1">
<div id="india_integration_section">
<block title="Indian Integration" id="india_localization" invisible="country_code != 'IN'">
<setting name="india_production_setting"
string="Production Environment"
help="Activate this to start using Indian services in the production environment.">
<field name="l10n_in_edi_production_env"/>
<div class='mt8'
invisible="not l10n_in_edi_production_env or (not module_l10n_in_edi and not module_l10n_in_gstin_status and not module_l10n_in_edi_ewaybill)">
<button name="l10n_in_edi_buy_iap"
title="Costs 1 credit per transaction. Free 200 credits will be available for the first time."
icon="fa-arrow-right"
type="object"
string="Buy credits"
class="btn-link"/>
</div>
</setting>
<setting/>
<setting name="electronic_invoices_in"
help="Connect to NIC (National Informatics Center) to submit invoices on posting."
company_dependent="1"
invisible="country_code != 'IN'"
documentation="/applications/finance/fiscal_localizations/india.html#india-e-invoicing">
<field name="module_l10n_in_edi"/>
</setting>
<setting name="electronic_waybill_in"
help="Connect to NIC (National Informatics Center) to submit e-waybill on posting."
company_dependent="1"

invisible="country_code != 'IN'"
documentation="/applications/finance/fiscal_localizations/india.html#indian-e-waybill">
<field name="module_l10n_in_edi_ewaybill" class="oe_inline" widget="upgrade_boolean"/>
</setting>
<setting name="india_gstin_status_api_settings"
string="Check GST Number Status"
help="Enable this to check the GST Number status"
invisible="country_code != 'IN'">
<field name="module_l10n_in_gstin_status" class="oe_inline"/>
</setting>
</block>

</div>
</xpath>
<app name="account" position="inside">
Expand Down
1 change: 0 additions & 1 deletion addons/l10n_in_edi/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
"depends": [
"account_edi",
"l10n_in",
"iap",
],
"description": """
Indian - E-invoicing
Expand Down
19 changes: 6 additions & 13 deletions addons/l10n_in_edi/models/account_edi_format.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,6 @@

_logger = logging.getLogger(__name__)

DEFAULT_IAP_ENDPOINT = "https://l10n-in-edi.api.odoo.com"
DEFAULT_IAP_TEST_ENDPOINT = "https://l10n-in-edi-demo.api.odoo.com"


class AccountEdiFormat(models.Model):
_inherit = "account.edi.format"
Expand Down Expand Up @@ -650,21 +647,17 @@ def _l10n_in_edi_get_token(self, company):

@api.model
def _l10n_in_edi_connect_to_server(self, company, url_path, params):
user_token = self.env["iap.account"].get("l10n_in_edi")
params.update({
"account_token": user_token.account_token,
"dbuuid": self.env["ir.config_parameter"].sudo().get_param("database.uuid"),
"username": company.sudo().l10n_in_edi_username,
"gstin": company.vat,
})
if company.sudo().l10n_in_edi_production_env:
default_endpoint = DEFAULT_IAP_ENDPOINT
else:
default_endpoint = DEFAULT_IAP_TEST_ENDPOINT
endpoint = self.env["ir.config_parameter"].sudo().get_param("l10n_in_edi.endpoint", default_endpoint)
url = "%s%s" % (endpoint, url_path)
try:
return jsonrpc(url, params=params, timeout=25)
return self.env['iap.account']._l10n_in_connect_to_server(
company.sudo().l10n_in_edi_production_env,
params,
url_path,
"l10n_in_edi.endpoint"
)
except AccessError as e:
_logger.warning("Connection error: %s", e.args[0])
return {
Expand Down
5 changes: 0 additions & 5 deletions addons/l10n_in_edi/models/res_company.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,6 @@ class ResCompany(models.Model):
l10n_in_edi_password = fields.Char("E-invoice (IN) Password", groups="base.group_system")
l10n_in_edi_token = fields.Char("E-invoice (IN) Token", groups="base.group_system")
l10n_in_edi_token_validity = fields.Datetime("E-invoice (IN) Valid Until", groups="base.group_system")
l10n_in_edi_production_env = fields.Boolean(
string="E-invoice (IN) Is production OSE environment",
help="Enable the use of production credentials",
groups="base.group_system",
)

def _l10n_in_edi_token_is_valid(self):
self.ensure_one()
Expand Down
14 changes: 0 additions & 14 deletions addons/l10n_in_edi/models/res_config_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,6 @@ class ResConfigSettings(models.TransientModel):

l10n_in_edi_username = fields.Char("Indian EDI username", related="company_id.l10n_in_edi_username", readonly=False)
l10n_in_edi_password = fields.Char("Indian EDI password", related="company_id.l10n_in_edi_password", readonly=False)
l10n_in_edi_production_env = fields.Boolean(
string="Indian EDI Testing Environment",
related="company_id.l10n_in_edi_production_env",
readonly=False
)

def l10n_in_check_gst_number(self):
if not self.company_id.vat:
Expand All @@ -41,12 +36,3 @@ def l10n_in_edi_test(self):
'message': _("API credentials validated successfully"),
}
}

def l10n_in_edi_buy_iap(self):
if not self.l10n_in_edi_production_env:
raise UserError(_("You must enable production environment to buy credits"))
return {
'type': 'ir.actions.act_url',
'url': self.env["iap.account"].get_credits_url(service_name="l10n_in_edi", base_url=''),
'target': '_new'
}
18 changes: 3 additions & 15 deletions addons/l10n_in_edi/views/res_config_settings_views.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,8 @@
<field name="model">res.config.settings</field>
<field name="inherit_id" ref="account.res_config_settings_view_form"/>
<field name="arch" type="xml">
<div id="india_integration_section" position="attributes">
<attribute name="invisible">0</attribute>
</div>
<xpath expr="//block[@id='india_localization']" position="inside">
<setting id="gsp_setting" string="Setup E-invoice" help="Check the documentation to get credentials" documentation="/applications/finance/fiscal_localizations/india.html" company_dependent="1" >
<div class="content-group">
<xpath expr="//setting[@name='electronic_invoices_in']" position="inside">
<div class="content-group" invisible="not module_l10n_in_edi">
<div class="mt16 row">
<label for="l10n_in_edi_username" string="Username" class="col-3 col-lg-3 o_light_label"/>
<field name="l10n_in_edi_username" nolabel="1"/>
Expand All @@ -19,18 +15,10 @@
<label for="l10n_in_edi_password" string="Password" class="col-3 col-lg-3 o_light_label"/>
<field name="l10n_in_edi_password" password="True" nolabel="1"/>
</div>
<div class="row">
<label for="l10n_in_edi_production_env" string="Production Environment" class="col-3 col-lg-3 o_light_label"/>
<field name="l10n_in_edi_production_env" nolabel="1"/>
</div>
</div>
<div class='mt8'>
<div class='mt8' invisible="not module_l10n_in_edi">
<button name="l10n_in_edi_test" icon="oi-arrow-right" type="object" string="Verify Username and Password" class="btn-link"/>
</div>
<div class='mt8'>
<button name="l10n_in_edi_buy_iap" title="Costs 1 credit per transaction. Free 200 credits will be available for the first time." icon="fa-arrow-right" type="object" string="Buy credits" class="btn-link"/>
</div>
</setting>
</xpath>
</field>
</record>
Expand Down
19 changes: 7 additions & 12 deletions addons/l10n_in_edi_ewaybill/models/account_edi_format.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@
from odoo import models, fields, api, _
from odoo.tools import html_escape
from odoo.exceptions import AccessError
from odoo.addons.iap import jsonrpc
from odoo.addons.l10n_in_edi.models.account_edi_format import DEFAULT_IAP_ENDPOINT, DEFAULT_IAP_TEST_ENDPOINT

from .error_codes import ERROR_CODES

Expand Down Expand Up @@ -586,21 +584,18 @@ def _l10n_in_set_missing_error_message(self, response):

@api.model
def _l10n_in_edi_ewaybill_connect_to_server(self, company, url_path, params):
user_token = self.env["iap.account"].get("l10n_in_edi")
params.update({
"account_token": user_token.account_token,
"dbuuid": self.env["ir.config_parameter"].sudo().get_param("database.uuid"),
"username": company.sudo().l10n_in_edi_ewaybill_username,
"gstin": company.vat,
})
if company.sudo().l10n_in_edi_production_env:
default_endpoint = DEFAULT_IAP_ENDPOINT
else:
default_endpoint = DEFAULT_IAP_TEST_ENDPOINT
endpoint = self.env["ir.config_parameter"].sudo().get_param("l10n_in_edi_ewaybill.endpoint", default_endpoint)
url = "%s%s" % (endpoint, url_path)
try:
response = jsonrpc(url, params=params, timeout=70)
response = self.env['iap.account']._l10n_in_connect_to_server(
is_production=company.sudo().l10n_in_edi_production_env,
params=params,
url_path=url_path,
config_parameter="l10n_in_edi_ewaybill.endpoint",
timeout=70
)
return self._l10n_in_set_missing_error_message(response)
except AccessError as e:
_logger.warning("Connection error: %s", e.args[0])
Expand Down
36 changes: 12 additions & 24 deletions addons/l10n_in_edi_ewaybill/views/res_config_settings_views.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,32 +5,20 @@
<field name="model">res.config.settings</field>
<field name="inherit_id" ref="account.res_config_settings_view_form"/>
<field name="arch" type="xml">
<div id="india_integration_section" position="attributes">
<attribute name="invisible">0</attribute>
</div>
<xpath expr="//block[@id='india_localization']" position="inside">
<setting id="l10n_in_eway_iap" string="Setup Electronic Waybill" help="Check the documentation to get credentials." documentation="/applications/finance/fiscal_localizations/india.html" company_dependent="1" >
<div class="content-group">
<div class="mt16 row">
<label for="l10n_in_edi_ewaybill_username" string="Username" class="col-3 col-lg-3 o_light_label"/>
<field name="l10n_in_edi_ewaybill_username" nolabel="1"/>
</div>
<div class="row">
<label for="l10n_in_edi_ewaybill_password" string="Password" class="col-3 col-lg-3 o_light_label"/>
<field name="l10n_in_edi_ewaybill_password" password="True" nolabel="1"/>
</div>
<div class="row">
<label for="l10n_in_edi_production_env" string="Production Environment" class="col-3 col-lg-3 o_light_label"/>
<field name="l10n_in_edi_production_env" nolabel="1"/>
</div>
<xpath expr="//setting[@name='electronic_waybill_in']" position="inside">
<div class="content-group" invisible="not module_l10n_in_edi_ewaybill or not module_l10n_in_edi">
<div class="mt16 row">
<label for="l10n_in_edi_ewaybill_username" string="Username" class="col-3 col-lg-3 o_light_label"/>
<field name="l10n_in_edi_ewaybill_username" nolabel="1"/>
</div>
<div class='mt8'>
<button name="l10n_in_edi_ewaybill_test" icon="oi-arrow-right" type="object" string="Verify Username and Password" class="btn-link"/>
<div class="row">
<label for="l10n_in_edi_ewaybill_password" string="Password" class="col-3 col-lg-3 o_light_label"/>
<field name="l10n_in_edi_ewaybill_password" password="True" nolabel="1"/>
</div>
<div class='mt8'>
<button name="l10n_in_edi_buy_iap" title="Costs 1 credit per transaction. Free 200 credits will be available for the first time." icon="fa-arrow-right" type="object" string="Buy credits" class="btn-link"/>
</div>
</setting>
</div>
<div class='mt8' invisible="not module_l10n_in_edi_ewaybill or not module_l10n_in_edi">
<button name="l10n_in_edi_ewaybill_test" icon="oi-arrow-right" type="object" string="Verify Username and Password" class="btn-link"/>
</div>
</xpath>
</field>
</record>
Expand Down
19 changes: 7 additions & 12 deletions addons/l10n_in_ewaybill_stock/tools/ewaybill_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@
from markupsafe import Markup

from odoo import fields, _
from odoo.addons.iap import jsonrpc
from odoo.exceptions import AccessError
from odoo.addons.l10n_in_edi.models.account_edi_format import DEFAULT_IAP_ENDPOINT, DEFAULT_IAP_TEST_ENDPOINT
from odoo.addons.l10n_in_edi_ewaybill.models.error_codes import ERROR_CODES


Expand Down Expand Up @@ -58,21 +56,18 @@ def __init__(self, company):
self.env = self.company.env

def _ewaybill_jsonrpc_to_server(self, url_path, params):
user_token = self.env["iap.account"].get("l10n_in_edi")
params.update({
"account_token": user_token.account_token,
"dbuuid": self.env["ir.config_parameter"].sudo().get_param("database.uuid"),
"username": self.company.sudo().l10n_in_edi_ewaybill_username,
"gstin": self.company.vat,
})
if self.company.sudo().l10n_in_edi_production_env:
default_endpoint = DEFAULT_IAP_ENDPOINT
else:
default_endpoint = DEFAULT_IAP_TEST_ENDPOINT
endpoint = self.env["ir.config_parameter"].sudo().get_param("l10n_in_edi_ewaybill.endpoint", default_endpoint)
url = f"{endpoint}{url_path}"
try:
response = jsonrpc(url, params=params, timeout=10)
response = self.env['iap.account']._l10n_in_connect_to_server(
is_production=self.company.sudo().l10n_in_edi_production_env,
params=params,
url_path=url_path,
config_parameter="l10n_in_edi_ewaybill.endpoint",
timeout=10
)
if response.get('error'):
raise EWayBillError(response)
except AccessError as e:
Expand Down
3 changes: 3 additions & 0 deletions addons/l10n_in_gstin_status/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.

from . import models
Loading

0 comments on commit 2952b90

Please sign in to comment.