odoo18/addons/l10n_rs_edi/models/account_move.py

166 lines
6.0 KiB
Python

import uuid
import requests
from odoo import _, api, fields, models
from requests.exceptions import Timeout, ConnectionError, HTTPError
DEMO_EFAKTURA_URL = 'https://demoefaktura.mfin.gov.rs/api/publicApi/sales-invoice/ubl'
EFAKTURA_URL = 'https://efakturadev.mfin.gov.rs/api/publicApi/sales-invoice/ubl'
class AccountMove(models.Model):
_inherit = 'account.move'
l10n_rs_edi_uuid = fields.Char(
string="RS Invoice UUID",
compute="_compute_l10n_rs_edi_uuid",
copy=False,
store=True,
help="Unique Identifier for an invoice used as request id",
)
l10n_rs_edi_is_eligible = fields.Boolean(
compute="_compute_l10n_rs_edi_is_eligible",
store=True,
help="Technical field to determine if this invoice is eligible to be e-invoiced.",
)
l10n_rs_edi_attachment_file = fields.Binary(
string="Serbian E-Invoice XML File",
copy=False,
attachment=True,
help="Serbia: technical field holding the e-invoice XML data.",
)
l10n_rs_edi_attachment_id = fields.Many2one(
comodel_name='ir.attachment',
string="eFaktura XML Attachment",
compute=lambda self: self._compute_linked_attachment_id('l10n_rs_edi_attachment_id', 'l10n_rs_edi_attachment_file'),
depends=['l10n_rs_edi_attachment_file'],
)
l10n_rs_edi_state = fields.Selection(
string="Serbia E-Invoice state",
selection=[
('sent', 'Sent'),
('sending_failed', 'Error'),
],
tracking=True,
readonly=True,
copy=False,
)
l10n_rs_edi_error = fields.Text(
string="Serbia E-Invoice error",
copy=False,
readonly=True,
)
l10n_rs_tax_date_obligations_code = fields.Selection(
string="Tax Date Obligations",
selection=[
('35', 'By Delivery Date'),
('3', 'By Issuance Date'),
('432', 'By Billing System'),
],
store=True,
readonly=False,
compute='_compute_l10n_rs_tax_date_obligations_code',
)
l10n_rs_edi_invoice = fields.Char(string="Invoice Id", copy=False)
l10n_rs_edi_sales_invoice = fields.Char(string="Sales Invoice Id", copy=False)
l10n_rs_edi_purchase_invoice = fields.Char(string="Purchase Invoice Id", copy=False)
@api.depends('country_code', 'move_type')
def _compute_show_delivery_date(self):
# EXTENDS 'account'
super()._compute_show_delivery_date()
for move in self:
if move.l10n_rs_edi_is_eligible:
move.show_delivery_date = True
@api.depends("country_code", "move_type")
def _compute_l10n_rs_edi_is_eligible(self):
for move in self:
move.l10n_rs_edi_is_eligible = move.country_code == 'RS' and move.is_sale_document() and move.l10n_rs_edi_state in (False, 'sending_failed')
@api.depends('country_code')
def _compute_l10n_rs_tax_date_obligations_code(self):
for move in self:
if move.country_code == 'RS':
move.l10n_rs_tax_date_obligations_code = '3'
@api.depends('l10n_rs_edi_state')
def _compute_show_reset_to_draft_button(self):
# EXTENDS 'account'
super()._compute_show_reset_to_draft_button()
for move in self:
if move.show_reset_to_draft_button and move.l10n_rs_edi_state == 'sent':
move.show_reset_to_draft_button = False
@api.depends('l10n_rs_edi_is_eligible')
def _compute_l10n_rs_edi_uuid(self):
for move in self:
if move.l10n_rs_edi_is_eligible and not move.l10n_rs_edi_uuid:
move.l10n_rs_edi_uuid = uuid.uuid4()
def button_draft(self):
# EXTENDS 'account'
self.write({
"l10n_rs_edi_error": False,
"l10n_rs_edi_state": False,
})
return super().button_draft()
def _l10n_rs_edi_send(self, send_to_cir):
self.ensure_one()
self.env['res.company']._with_locked_records(self)
xml, errors = self.env['account.edi.xml.ubl.rs']._export_invoice(self)
if errors:
return xml, errors
params = {
'requestId': self.l10n_rs_edi_uuid,
'sendToCir': 'Yes' if send_to_cir else 'No'
}
url = DEMO_EFAKTURA_URL if self.company_id.l10n_rs_edi_demo_env else EFAKTURA_URL
headers = {
'Content-Type': 'application/xml',
'ApiKey': self.company_id.l10n_rs_edi_api_key,
}
error_message = False
try:
response = requests.post(url=url, params=params, headers=headers, data=xml, timeout=30)
response.raise_for_status()
except (Timeout, ConnectionError, HTTPError) as exception:
error_message = _("There was a problem with the connection with eFaktura: %s", exception)
self.message_post(body=error_message)
return xml, error_message
dict_response = {}
try:
dict_response = response.json()
except requests.exceptions.JSONDecodeError as e:
error_message = _("Invalid response from eFaktura: %s", str(e))
self.l10n_rs_edi_state = 'sending_failed' if error_message else 'sent'
self.l10n_rs_edi_error = error_message
self.l10n_rs_edi_invoice = dict_response.get('InvoiceId')
self.l10n_rs_edi_purchase_invoice = dict_response.get('PurchaseInvoiceId')
self.l10n_rs_edi_sales_invoice = dict_response.get('SalesInvoiceId')
return xml, error_message
def _l10n_rs_edi_get_attachment_values(self, xml):
self.ensure_one()
return {
'name': self._l10n_rs_edi_get_xml_attachment_name(),
'mimetype': 'application/xml',
'description': _('RS E-Invoice: %s', self.move_type),
'company_id': self.company_id.id,
'res_id': self.id,
'res_model': self._name,
'res_field': 'l10n_rs_edi_attachment_file',
'raw': xml,
'type': 'binary',
}
def _l10n_rs_edi_get_xml_attachment_name(self):
return f"{self.name.replace('/', '_')}_edi.xml"