# Part of Odoo. See LICENSE file for full copyright and licensing details. from datetime import timedelta from odoo import fields, models, _ from odoo.exceptions import RedirectWarning, UserError class L10n_InGstOtpValidation(models.TransientModel): _name = 'l10n_in.gst.otp.validation' _description = 'GST portal validation.' company_id = fields.Many2one('res.company', string="Company Name") gst_otp = fields.Char("OTP", copy=False) gst_token = fields.Char("GST Token", readonly=True) def check_gst_number(self): if not self.company_id.vat: action = { "view_mode": "form", "res_model": "res.company", "type": "ir.actions.act_window", "res_id": self.company_id.id, "views": [[self.env.ref("base.view_company_form").id, "form"]], } raise RedirectWarning(_("Please enter a GST number in company."), action, _('Go to Company')) def _l10n_in_reports_gstr_check_gst_token(self): """Check if another company with the same GST number has a valid GST token. If found, raises a RedirectWarning to guide the user to the Tax Units view. """ count = self.env['res.company'].sudo().search_count([ ('id', '!=', self.company_id.id), ('vat', '=', self.company_id.vat), ('l10n_in_gstr_gst_token_validity', '>', fields.Datetime.now()), ], limit=1) if count < 1: return message = _( "Another company is already using this GST number with " "a valid GST token. To proceed, make sure this company is " "part of a Tax Unit.", ) action = self.env.ref('account_reports.action_view_tax_units') raise RedirectWarning(message, action.id, _('Go to Tax units')) def gst_send_otp(self): self.check_gst_number() self._l10n_in_reports_gstr_check_gst_token() response = self.env["account.return"]._l10n_in_gstr_otp_request(self.company_id) error_message_list = [] for error in response.get('error', []): code = error.get('code') message = error.get('message') if code == 'AUTH4041': message = self.env._( "%(message)s\nHint: Please confirm that GST E-filing Username(%(username)s) is associated with GST Number(%(vat)s).", message=message, username=self.company_id.l10n_in_gstr_gst_username, vat=self.company_id.vat, ) error_message_list.append(f"[{code}] {message}") if error_message_list: raise UserError("\n".join(error_message_list)) self.gst_token = response.get("txn") form = self.env.ref("l10n_in_reports.view_validate_otp_gstr") return { "name": _("OTP Request"), "type": "ir.actions.act_window", "res_model": "l10n_in.gst.otp.validation", "res_id": self.id, "views": [[form.id, "form"]], "target": "new", "context": self.env.context, } def validate_otp(self): response = self.env["account.return"]._l10n_in_gstr_otp_auth_request( company=self.company_id, transaction=self.gst_token, otp=self.gst_otp) if response.get('error'): error_codes = [e.get('code') for e in response["error"]] if 'AUTH4033' in error_codes: raise UserError(_("Invalid OTP")) message = "\n".join(["[%s] %s" % (e.get('code'), e.get('message')) for e in response["error"]]) raise UserError(_('%s', message)) self.company_id.sudo().write({ "l10n_in_gstr_gst_token": self.gst_token, "l10n_in_gstr_gst_token_validity": fields.Datetime.now() + timedelta(hours=6) }) # trigger token refresh cron before expired cron = self.env.ref("l10n_in_reports.ir_cron_auto_refresh_gst_token") if cron.sudo().interval_type == 'hours': cron.sudo().write({'interval_type': 'months', 'interval_number': 9999}) cron._trigger(fields.Datetime.now() + timedelta(hours=5, minutes=54)) return { 'type': 'ir.actions.client', 'tag': 'display_notification', 'params': { 'type': 'info', 'sticky': False, 'message': _("API credentials validated successfully"), 'next': {'type': 'ir.actions.act_window_close'}, } } def validate_otp_and_do_next_action(self): if not (self.gst_otp and self.gst_otp.isdigit() and len(self.gst_otp) == 6): return { 'type': 'ir.actions.client', 'tag': 'display_notification', 'params': { 'type': 'danger', 'message': _("Invalid OTP. Please enter a valid 6-digit OTP"), } } return self.validate_otp()