diff --git a/.copier-answers.yml b/.copier-answers.yml new file mode 100644 index 00000000..37975f91 --- /dev/null +++ b/.copier-answers.yml @@ -0,0 +1,12 @@ +# Do NOT update manually; changes here will be overwritten by Copier +_commit: 719579f +_src_path: https://github.com/ingadhoc/addons-repo-template.git +description: Repositoriy for odoo addons of argentinian localization for functionalities + that are in odoo enterprise. For any argentinan functionality that is not in odoo + CE (community edition) or odoo EE (odoo enterprise), please refer to https://github.com/ingadhoc/odoo-argentina +is_private: false +name: odoo-argentina-ce +odoo_version: 18.0 +pre_commit_ignore: [] +slug: '' + diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml new file mode 100644 index 00000000..0915d8c3 --- /dev/null +++ b/.github/workflows/pre-commit.yml @@ -0,0 +1,34 @@ +# ⚠️ DO NOT EDIT THIS FILE, IT IS GENERATED BY COPIER ⚠️ +# Changes here will be lost on a future update. +# See: https://github.com/ingadhoc/addons-repo-template + +name: pre-commit + +on: + push: + branches: "*.0" + pull_request: + +jobs: + pre-commit: + runs-on: ubuntu-latest + steps: + - + name: Checkout + uses: actions/checkout@v4 + - + id: setup-python + name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: "3.10" + cache: "pip" + - + name: Pre-commit cache + uses: actions/cache@v4 + with: + path: ~/.cache/pre-commit + key: pre-commit|${{ steps.setup-python.outputs.python-version }}|${{ hashFiles('.pre-commit-config.yaml') }} + - + name: Pre-commit + uses: pre-commit/action@v3.0.1 diff --git a/.gitignore b/.gitignore index f370d7a8..a6d076d8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,7 @@ +# ⚠️ DO NOT EDIT THIS FILE, IT IS GENERATED BY COPIER ⚠️ +# Changes here will be lost on a future update. +# See: https://github.com/ingadhoc/addons-repo-template + # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] @@ -37,6 +41,12 @@ coverage.xml # Translations *.mo +# Ensure we never commit pgdumps +*.dump +*.sql +*.pg +*.pg.gpg + # Mr Developer .mr.developer.cfg .project @@ -50,4 +60,3 @@ coverage.xml # Sphinx documentation docs/_build/ - diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..0b82f519 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,69 @@ +# ⚠️ DO NOT EDIT THIS FILE, IT IS GENERATED BY COPIER ⚠️ +# Changes here will be lost on a future update. +# See: https://github.com/ingadhoc/addons-repo-template + +exclude: | + (?x) + + # We don't want to mess with tool-generated files + .svg$|/tests/([^/]+/)?cassettes/|^.copier-answers.yml$|^.github/|^eslint.config.cjs|^prettier.config.cjs| + # Library files can have extraneous formatting (even minimized) + /static/(src/)?lib/| + # Ignore build and dist directories in addons + /build/|/dist/| + # Ignore test files in addons + /tests/samples/.*| + # You don't usually want a bot to modify your legal texts + (LICENSE.*|COPYING.*) + +# Keep in sync with .github/workflows/pre-commit.yml +default_language_version: + python: python3.10 + +repos: + + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v5.0.0 + hooks: + - id: check-added-large-files + - id: check-case-conflict + - id: check-docstring-first + - id: check-executables-have-shebangs + - id: check-merge-conflict + - id: check-symlinks + - id: check-xml + - id: check-yaml + - id: debug-statements + - id: end-of-file-fixer + - id: mixed-line-ending + args: ["--fix=lf"] + - id: trailing-whitespace + # exclude autogenerated files + exclude: \.pot?$ + + - repo: https://github.com/OCA/odoo-pre-commit-hooks + rev: v0.0.35 + hooks: + - id: oca-checks-odoo-module + args: + - --disable=xml-dangerous-qweb-replace-low-priority,xml-view-dangerous-replace-low-priority,xml-oe-structure-missing-id + - id: oca-checks-po + args: + - --disable=po-pretty-format + + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.6.8 + hooks: + - id: ruff + args: [--fix, --exit-non-zero-on-fix] + - id: ruff-format + + - repo: https://github.com/OCA/pylint-odoo + rev: v9.1.3 + hooks: + - id: pylint_odoo + + - repo: https://github.com/rstcheck/rstcheck + rev: v6.2.1 + hooks: + - id: rstcheck diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..277f7c98 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "python.languageServer": "None" +} diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md index 1438139e..77893ddc 100644 --- a/ISSUE_TEMPLATE.md +++ b/ISSUE_TEMPLATE.md @@ -1,22 +1,22 @@ > **No me gusta mi camisa** > > *Version affectada:* -> +> > - 7.0 y encima -> +> > *Pasos para reproducir:* -> +> > 1. ponerse antes de un espejo > 2. prender la luz > 3. abrir los ojos -> +> > *Lo que pasa actualmente:* -> +> > - Asusto -> +> > *Lo que debe pasar:* -> -> - Todo bien, listo para la fiesta +> +> - Todo bien, listo para la fiesta > > *Analisis profunda:* > diff --git a/README.md b/README.md index a29d8b11..f7b5656a 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,12 @@ -[![Coverage Status](https://coveralls.io/repos/ingadhoc/odoo-argentina/badge.png?branch=15.0)](https://coveralls.io/r/ingadhoc/odoo-argentina?branch=15.0) -[![Code Climate](https://codeclimate.com/github/ingadhoc/odoo-argentina/badges/gpa.svg)](https://codeclimate.com/github/ingadhoc/odoo-argentina) +[![Coverage Status](https://coveralls.io/repos/ingadhoc//badge.png?branch=18.0)](https://coveralls.io/r/ingadhoc/?branch=18.0) +[![Code Climate](https://codeclimate.com/github/ingadhoc//badges/gpa.svg)](https://codeclimate.com/github/ingadhoc/) -# ADHOC odoo-argentina-ce +# odoo-argentina-ce -Repositoriy for odoo addons of argentinian localization for functionalities that are in odoo enterprise. -For any argentinan functionality that is not in odoo CE (community edition) or odoo EE (odoo enterprise), please refer to https://github.com/ingadhoc/odoo-argentina - -[//]: # (addons) -[//]: # (end addons) - -Translation Status ------------------- -[![Transifex Status](https://www.transifex.com/projects/p/ingadhoc-odoo-argentina-15-0/chart/image_png)](https://www.transifex.com/projects/p/ingadhoc-odoo-argentina-15-0) +Repositoriy for odoo addons of argentinian localization for functionalities that are in odoo enterprise. For any argentinan functionality that is not in odoo CE (community edition) or odoo EE (odoo enterprise), please refer to https://github.com/ingadhoc/odoo-argentina ---- ADHOC **Adhoc SA** - www.adhoc.com.ar +. diff --git a/l10n_ar_afipws/__manifest__.py b/l10n_ar_afipws/__manifest__.py index 8c52f3d6..d5b9b4d0 100644 --- a/l10n_ar_afipws/__manifest__.py +++ b/l10n_ar_afipws/__manifest__.py @@ -28,7 +28,7 @@ "demo/parameter_demo.xml", ], "images": [], - 'installable': True, + "installable": True, "auto_install": False, "application": False, } diff --git a/l10n_ar_afipws/models/afipws_certificate.py b/l10n_ar_afipws/models/afipws_certificate.py index f260ba52..304d0f72 100644 --- a/l10n_ar_afipws/models/afipws_certificate.py +++ b/l10n_ar_afipws/models/afipws_certificate.py @@ -2,8 +2,8 @@ # For copyright and license notices, see __manifest__.py file in module root # directory ############################################################################## +from odoo import _, api, fields, models from odoo.exceptions import UserError -from odoo import fields, models, api, _ try: from OpenSSL import crypto @@ -44,7 +44,6 @@ class AfipwsCertificate(models.Model): ("confirmed", "Confirmed"), ("cancel", "Cancelled"), ], - "State", index=True, readonly=True, default="draft", @@ -98,10 +97,7 @@ def verify_crt(self): msg = False if not crt: - msg = _( - "Invalid action! Please, set the certification string to " - "continue." - ) + msg = _("Invalid action! Please, set the certification string to " "continue.") certificate = rec.get_certificate() if certificate is None: msg = _( @@ -120,9 +116,7 @@ def get_certificate(self): self.ensure_one() if self.crt: try: - certificate = crypto.load_certificate( - crypto.FILETYPE_PEM, self.crt.encode("ascii") - ) + certificate = crypto.load_certificate(crypto.FILETYPE_PEM, self.crt.encode("ascii")) except Exception as e: if "Expecting: CERTIFICATE" in e[0]: raise UserError( @@ -132,9 +126,7 @@ def get_certificate(self): ) ) else: - raise UserError( - _("Unknown error.\nX509 return this message:\n %s") % (e[0]) - ) + raise UserError(_("Unknown error.\nX509 return this message:\n %s") % (e[0])) else: certificate = None return certificate diff --git a/l10n_ar_afipws/models/afipws_certificate_alias.py b/l10n_ar_afipws/models/afipws_certificate_alias.py index cc3d1a42..2bd57cd8 100644 --- a/l10n_ar_afipws/models/afipws_certificate_alias.py +++ b/l10n_ar_afipws/models/afipws_certificate_alias.py @@ -2,7 +2,7 @@ # For copyright and license notices, see __manifest__.py file in module root # directory ############################################################################## -from odoo import fields, models, api, _ +from odoo import _, api, fields, models from odoo.exceptions import ValidationError try: @@ -31,7 +31,6 @@ class AfipwsCertificateAlias(models.Model): """ common_name = fields.Char( - "Common Name", size=64, default="AFIP WS", help="Just a name, you can leave it this way", @@ -63,12 +62,10 @@ class AfipwsCertificateAlias(models.Model): readonly=True, ) city = fields.Char( - "City", readonly=True, required=True, ) department = fields.Char( - "Department", default="IT", readonly=True, required=True, @@ -96,7 +93,6 @@ class AfipwsCertificateAlias(models.Model): ) service_type = fields.Selection( [("in_house", "In House"), ("outsourced", "Outsourced")], - "Service Type", default="in_house", required=True, readonly=True, @@ -120,7 +116,6 @@ class AfipwsCertificateAlias(models.Model): ) type = fields.Selection( [("production", "Production"), ("homologation", "Homologation")], - "Type", required=True, default="production", readonly=True, @@ -184,9 +179,7 @@ def action_create_certificate_request(self): req.get_subject().O = self.company_id.name.encode("ascii", "ignore") req.get_subject().OU = self.department.encode("ascii", "ignore") req.get_subject().CN = self.common_name.encode("ascii", "ignore") - req.get_subject().serialNumber = "CUIT %s" % self.cuit.encode( - "ascii", "ignore" - ) + req.get_subject().serialNumber = "CUIT %s" % self.cuit.encode("ascii", "ignore") k = crypto.load_privatekey(crypto.FILETYPE_PEM, self.key) self.key = crypto.dump_privatekey(crypto.FILETYPE_PEM, k) req.set_pubkey(k) @@ -202,6 +195,4 @@ def action_create_certificate_request(self): @api.constrains("common_name") def check_common_name_len(self): if self.filtered(lambda x: x.common_name and len(x.common_name) > 50): - raise ValidationError( - _("The Common Name must be lower than 50 characters long") - ) + raise ValidationError(_("The Common Name must be lower than 50 characters long")) diff --git a/l10n_ar_afipws/models/afipws_connection.py b/l10n_ar_afipws/models/afipws_connection.py index 1b5de7ca..c553f8b0 100644 --- a/l10n_ar_afipws/models/afipws_connection.py +++ b/l10n_ar_afipws/models/afipws_connection.py @@ -2,15 +2,15 @@ # For copyright and license notices, see __manifest__.py file in module root # directory ############################################################################## -from odoo import fields, models, api, _ -from odoo.exceptions import UserError, RedirectWarning import logging +from odoo import _, api, fields, models +from odoo.exceptions import RedirectWarning, UserError + _logger = logging.getLogger(__name__) class AfipwsConnection(models.Model): - _name = "afipws.connection" _description = "AFIP WS Connection" _rec_name = "afip_ws" @@ -28,11 +28,9 @@ class AfipwsConnection(models.Model): readonly=True, ) token = fields.Text( - "Token", readonly=True, ) sign = fields.Text( - "Sign", readonly=True, ) generationtime = fields.Datetime("Generation Time", readonly=True) @@ -47,7 +45,6 @@ class AfipwsConnection(models.Model): ) type = fields.Selection( [("production", "Production"), ("homologation", "Homologation")], - "Type", required=True, ) afip_ws = fields.Selection( @@ -90,31 +87,17 @@ def get_afip_ws_url(self, afip_ws, environment_type): afip_ws_url = False if afip_ws == "ws_sr_padron_a4": if environment_type == "production": - afip_ws_url = ( - "https://aws.afip.gov.ar/sr-padron/webservices/" - "personaServiceA4?wsdl" - ) + afip_ws_url = "https://aws.afip.gov.ar/sr-padron/webservices/" "personaServiceA4?wsdl" else: - afip_ws_url = ( - "https://awshomo.afip.gov.ar/sr-padron/webservices/" - "personaServiceA4?wsdl" - ) + afip_ws_url = "https://awshomo.afip.gov.ar/sr-padron/webservices/" "personaServiceA4?wsdl" elif afip_ws == "ws_sr_padron_a5": if environment_type == "production": - afip_ws_url = ( - "https://aws.afip.gov.ar/sr-padron/webservices/" - "personaServiceA5?wsdl" - ) + afip_ws_url = "https://aws.afip.gov.ar/sr-padron/webservices/" "personaServiceA5?wsdl" else: - afip_ws_url = ( - "https://awshomo.afip.gov.ar/sr-padron/webservices/" - "personaServiceA5?wsdl" - ) + afip_ws_url = "https://awshomo.afip.gov.ar/sr-padron/webservices/" "personaServiceA5?wsdl" elif afip_ws == "wsfecred": if environment_type == "production": - afip_ws_url = ( - "https://serviciosjava.afip.gob.ar/wsfecred/FECredService?wsdl" - ) + afip_ws_url = "https://serviciosjava.afip.gob.ar/wsfecred/FECredService?wsdl" else: afip_ws_url = "https://fwshomo.afip.gov.ar/wsfecred/FECredService?wsdl" @@ -125,10 +108,7 @@ def check_afip_ws(self, afip_ws): self.ensure_one() if self.afip_ws != afip_ws: raise UserError( - _( - "This method is for %s connections and you call it from an" - " %s connection" - ) + _("This method is for %s connections and you call it from an" " %s connection") % (afip_ws, self.afip_ws) ) @@ -137,10 +117,7 @@ def connect(self): Method to be called """ self.ensure_one() - _logger.info( - "Getting connection to ws %s from libraries on " - "connection id %s" % (self.afip_ws, self.id) - ) + _logger.info("Getting connection to ws %s from libraries on " "connection id %s" % (self.afip_ws, self.id)) ws = self._get_ws(self.afip_ws) # parche por este error que da al consultar por esa opción de homo @@ -151,9 +128,7 @@ def connect(self): ws.HOMO = False if not ws: - raise UserError( - _("AFIP Webservice %s not implemented yet" % (self.afip_ws)) - ) + raise UserError(_("AFIP Webservice %s not implemented yet") % self.afip_ws) # TODO implementar cache y proxy # create the proxy and get the configuration system parameters: # cfg = self.pool.get('ir.config_parameter').sudo() @@ -170,17 +145,13 @@ def connect(self): "ExpatError" in repr(error) or "mismatched tag" in repr(error) or "Conexión reinicializada por la máquina remota" in repr(error) - or "module 'httplib2' has no attribute 'SSLHandshakeError'" - in repr(error) + or "module 'httplib2' has no attribute 'SSLHandshakeError'" in repr(error) ): action = self.env.ref("l10n_ar_afipws.action_afip_padron") - msg = _( - "It seems like AFIP service is not available.\nPlease try again later or try manually" - ) + msg = _("It seems like AFIP service is not available.\nPlease try again later or try manually") raise RedirectWarning(msg, action.id, _("Go and find data manually")) raise UserError( - "There was a connection problem to AFIP. Contact your Odoo Provider. Error\n\n%s" - % repr(error) + "There was a connection problem to AFIP. Contact your Odoo Provider. Error\n\n%s" % repr(error) ) cuit = self.company_id.partner_id.ensure_vat() diff --git a/l10n_ar_afipws/models/res_company.py b/l10n_ar_afipws/models/res_company.py index 76539727..c9238f84 100644 --- a/l10n_ar_afipws/models/res_company.py +++ b/l10n_ar_afipws/models/res_company.py @@ -2,23 +2,23 @@ # For copyright and license notices, see __manifest__.py file in module root # directory ############################################################################## -from odoo import fields, models, api, _ +import hashlib import logging -from odoo.exceptions import UserError -import dateutil.parser -import pytz -import odoo.tools as tools import os -import hashlib -import time import sys +import time import traceback +import dateutil.parser +import odoo.tools as tools +import pytz +from odoo import _, api, fields, models +from odoo.exceptions import UserError + _logger = logging.getLogger(__name__) class ResCompany(models.Model): - _inherit = "res.company" alias_ids = fields.One2many( @@ -46,9 +46,7 @@ def _get_environment_type(self): * 'test' or 'develop' --> homologation * other or no parameter --> production """ - parameter_env_type = ( - self.env["ir.config_parameter"].sudo().get_param("afip.ws.env.type") - ) + parameter_env_type = self.env["ir.config_parameter"].sudo().get_param("afip.ws.env.type") if parameter_env_type == "production": environment_type = "production" elif parameter_env_type == "homologation": @@ -112,9 +110,9 @@ def get_key_and_certificate(self, environment_type): if pkey_path and cert_path: try: if os.path.isfile(pkey_path) and os.path.isfile(cert_path): - with open(pkey_path, "r") as pkey_file: + with open(pkey_path) as pkey_file: pkey = pkey_file.read() - with open(cert_path, "r") as cert_file: + with open(cert_path) as cert_file: cert = cert_file.read() msg = "Could not find %s or %s files" % (pkey_path, cert_path) except Exception: @@ -127,9 +125,7 @@ def get_key_and_certificate(self, environment_type): def get_connection(self, afip_ws): self.ensure_one() - _logger.info( - "Getting connection for company %s and ws %s" % (self.name, afip_ws) - ) + _logger.info("Getting connection for company %s and ws %s" % (self.name, afip_ws)) now = fields.Datetime.now() environment_type = self._get_environment_type() @@ -173,14 +169,10 @@ def _create_connection(self, afip_ws, environment_type): ) auth_data["generationtime"] = ( - dateutil.parser.parse(auth_data["generationtime"]) - .astimezone(pytz.utc) - .replace(tzinfo=None) + dateutil.parser.parse(auth_data["generationtime"]).astimezone(pytz.utc).replace(tzinfo=None) ) auth_data["expirationtime"] = ( - dateutil.parser.parse(auth_data["expirationtime"]) - .astimezone(pytz.utc) - .replace(tzinfo=None) + dateutil.parser.parse(auth_data["expirationtime"]).astimezone(pytz.utc).replace(tzinfo=None) ) _logger.info("Successful Connection to AFIP.") @@ -213,12 +205,7 @@ def authenticate( DEFAULT_TTL = 60 * 60 * 5 # make md5 hash of the parameter for caching... - fn = ( - "%s.xml" - % hashlib.md5( - (service + certificate + private_key).encode("utf-8") - ).hexdigest() - ) + fn = "%s.xml" % hashlib.md5((service + certificate + private_key).encode("utf-8")).hexdigest() if cache: fn = os.path.join(cache, fn) else: @@ -226,10 +213,7 @@ def authenticate( try: # read the access ticket (if already authenticated) - if ( - not os.path.exists(fn) - or os.path.getmtime(fn) + (DEFAULT_TTL) < time.time() - ): + if not os.path.exists(fn) or os.path.getmtime(fn) + (DEFAULT_TTL) < time.time(): # access ticket (TA) outdated, create new access request # ticket (TRA) tra = wsaa.CreateTRA(service=service, ttl=DEFAULT_TTL) @@ -245,7 +229,7 @@ def authenticate( open(fn, "w").write(ta) else: # get the access ticket from the previously written file - ta = open(fn, "r").read() + ta = open(fn).read() # analyze the access ticket xml and extract the relevant fields wsaa.AnalizarXml(xml=ta) token = wsaa.ObtenerTagXml("token") @@ -260,12 +244,8 @@ def authenticate( err_msg = wsaa.Excepcion else: # avoid encoding problem when reporting exceptions to the user: - err_msg = traceback.format_exception_only(sys.exc_type, sys.exc_value)[ - 0 - ] - raise UserError( - _("Could not connect. This is the what we received: %s") % (err_msg) - ) + err_msg = traceback.format_exception_only(sys.exc_type, sys.exc_value)[0] + raise UserError(_("Could not connect. This is the what we received: %s") % (err_msg)) return { "uniqueid": uniqueId, "generationtime": generationTime, diff --git a/l10n_ar_afipws/models/res_config_settings.py b/l10n_ar_afipws/models/res_config_settings.py index a55da02b..90a46af5 100644 --- a/l10n_ar_afipws/models/res_config_settings.py +++ b/l10n_ar_afipws/models/res_config_settings.py @@ -6,7 +6,6 @@ class ResConfigSettings(models.TransientModel): - _inherit = "res.config.settings" afip_ws_env_type = fields.Selection( diff --git a/l10n_ar_afipws/models/res_partner.py b/l10n_ar_afipws/models/res_partner.py index d9bfc9a9..5fc9ca88 100644 --- a/l10n_ar_afipws/models/res_partner.py +++ b/l10n_ar_afipws/models/res_partner.py @@ -3,10 +3,11 @@ # directory ############################################################################## -from odoo import fields, models, api, _ -from odoo.exceptions import UserError import logging +from odoo import _, fields, models +from odoo.exceptions import UserError + _logger = logging.getLogger(__name__) @@ -24,7 +25,6 @@ class ResPartner(models.Model): # Separo esto para poder heredar de otros # modulos y extender los datos def parce_census_vals(self, census): - # porque imp_iva activo puede ser S o AC imp_iva = census.imp_iva if imp_iva == "S": @@ -53,10 +53,7 @@ def parce_census_vals(self, census): elif census.monotributo == "S": vals["imp_ganancias_padron"] = "NC" else: - _logger.info( - "We couldn't get impuesto a las ganancias from padron, you" - "must set it manually" - ) + _logger.info("We couldn't get impuesto a las ganancias from padron, you" "must set it manually") if census.provincia: # depending on the database, caba can have one of this codes @@ -81,22 +78,13 @@ def parce_census_vals(self, census): vals["state_id"] = state.id if imp_iva == "NI" and census.monotributo == "S": - vals["l10n_ar_afip_responsibility_type_id"] = self.env.ref( - "l10n_ar.res_RM" - ).id + vals["l10n_ar_afip_responsibility_type_id"] = self.env.ref("l10n_ar.res_RM").id elif imp_iva == "AC": - vals["l10n_ar_afip_responsibility_type_id"] = self.env.ref( - "l10n_ar.res_IVARI" - ).id + vals["l10n_ar_afip_responsibility_type_id"] = self.env.ref("l10n_ar.res_IVARI").id elif imp_iva == "EX": - vals["l10n_ar_afip_responsibility_type_id"] = self.env.ref( - "l10n_ar.res_IVAE" - ).id + vals["l10n_ar_afip_responsibility_type_id"] = self.env.ref("l10n_ar.res_IVAE").id else: - _logger.info( - "We couldn't infer the AFIP responsability from padron, you" - "must set it manually." - ) + _logger.info("We couldn't infer the AFIP responsability from padron, you" "must set it manually.") return vals @@ -110,9 +98,7 @@ def get_data_from_padron_afip(self): company = self.env.user.company_id env_type = company._get_environment_type() try: - certificate = company.get_key_and_certificate( - company._get_environment_type() - ) + certificate = company.get_key_and_certificate(company._get_environment_type()) except Exception: certificate = self.env["afipws.certificate"].search( [ diff --git a/l10n_ar_afipws/views/res_config_settings.xml b/l10n_ar_afipws/views/res_config_settings.xml index 19e5ab17..92f8b11f 100644 --- a/l10n_ar_afipws/views/res_config_settings.xml +++ b/l10n_ar_afipws/views/res_config_settings.xml @@ -1,6 +1,5 @@ - res.config.settings.view.form.inherit.account res.config.settings @@ -17,5 +16,4 @@ - diff --git a/l10n_ar_afipws/wizard/res_partner_update_from_padron_wizard.py b/l10n_ar_afipws/wizard/res_partner_update_from_padron_wizard.py index 14f8487d..9750118d 100644 --- a/l10n_ar_afipws/wizard/res_partner_update_from_padron_wizard.py +++ b/l10n_ar_afipws/wizard/res_partner_update_from_padron_wizard.py @@ -1,7 +1,8 @@ -from odoo import models, api, fields, _ +import logging from ast import literal_eval + +from odoo import _, api, fields, models from odoo.exceptions import UserError -import logging _logger = logging.getLogger(__name__) @@ -42,9 +43,7 @@ def default_get(self, fields): if context.get("active_model") == "res.partner" and context.get("active_ids"): partners = self.get_partners() if not partners: - raise UserError( - _("No se encontró ningún partner con CUIT para actualizar") - ) + raise UserError(_("No se encontró ningún partner con CUIT para actualizar")) elif len(partners) == 1: res["state"] = "selection" res["partner_id"] = partners[0].id @@ -67,11 +66,7 @@ def _get_domain(self): @api.model def _get_default_title_case(self): - parameter = ( - self.env["ir.config_parameter"] - .sudo() - .get_param("use_title_case_on_padron_afip") - ) + parameter = self.env["ir.config_parameter"].sudo().get_param("use_title_case_on_padron_afip") if parameter == "False" or parameter == "0": return False return True @@ -82,7 +77,6 @@ def get_fields(self): state = fields.Selection( [("option", "Option"), ("selection", "Selection"), ("finished", "Finished")], - "State", readonly=True, required=True, default="option", @@ -109,7 +103,6 @@ def get_fields(self): default=True, ) title_case = fields.Boolean( - string="Title Case", help="Converts retreived text fields to Title Case.", default=_get_default_title_case, ) diff --git a/l10n_ar_afipws/wizard/upload_certificate_wizard.py b/l10n_ar_afipws/wizard/upload_certificate_wizard.py index a88a99db..7e65cd7c 100644 --- a/l10n_ar_afipws/wizard/upload_certificate_wizard.py +++ b/l10n_ar_afipws/wizard/upload_certificate_wizard.py @@ -2,9 +2,10 @@ # For copyright and license notices, see __manifest__.py file in module root # directory ############################################################################## -from odoo import fields, api, models import base64 +from odoo import api, fields, models + class L10nArAfipwsUploadCertificate(models.TransientModel): _name = "afipws.upload_certificate.wizard" diff --git a/l10n_ar_afipws_fe/__manifest__.py b/l10n_ar_afipws_fe/__manifest__.py index 534ab9b4..8f7cf4d7 100644 --- a/l10n_ar_afipws_fe/__manifest__.py +++ b/l10n_ar_afipws_fe/__manifest__.py @@ -22,7 +22,7 @@ ], "demo": [], "images": [], - 'installable': True, + "installable": True, "auto_install": False, "application": False, } diff --git a/l10n_ar_afipws_fe/afip_utils.py b/l10n_ar_afipws_fe/afip_utils.py index 5ba1581e..67c58a50 100644 --- a/l10n_ar_afipws_fe/afip_utils.py +++ b/l10n_ar_afipws_fe/afip_utils.py @@ -1,4 +1,5 @@ from pysimplesoap.client import SimpleXMLElement + # import xml.etree.ElementTree as ET @@ -6,12 +7,12 @@ def _get_response_info(xml_response): return SimpleXMLElement(xml_response) -def get_invoice_number_from_response(xml_response, afip_ws='wsfe'): +def get_invoice_number_from_response(xml_response, afip_ws="wsfe"): if not xml_response: return False try: xml = _get_response_info(xml_response) - return int(xml('CbteDesde')) + return int(xml("CbteDesde")) # TODO por ahora usamos pysimplesoap porque es mas comodo # Sino generar una estrategia recusiva para todos los tipos de WS # namespaces = { diff --git a/l10n_ar_afipws_fe/models/account_journal.py b/l10n_ar_afipws_fe/models/account_journal.py index b74472a8..58adae48 100644 --- a/l10n_ar_afipws_fe/models/account_journal.py +++ b/l10n_ar_afipws_fe/models/account_journal.py @@ -2,8 +2,9 @@ # For copyright and license notices, see __manifest__.py file in module root # directory ############################################################################## -from odoo import models, api, fields, _ import logging + +from odoo import _, api, fields, models from odoo.exceptions import UserError _logger = logging.getLogger(__name__) @@ -12,9 +13,7 @@ class AccountJournal(models.Model): _inherit = "account.journal" - afip_ws = fields.Selection( - selection="_get_afip_ws", compute="_compute_afip_ws", string="AFIP WS" - ) + afip_ws = fields.Selection(selection="_get_afip_ws", compute="_compute_afip_ws", string="AFIP WS") def _get_afip_ws(self): return [ @@ -85,9 +84,7 @@ def action_get_connection(self): "type": "ir.actions.client", "tag": "display_notification", "params": { - "title": _( - "Great, everything seems fine. The connection did not fail." - ), + "title": _("Great, everything seems fine. The connection did not fail."), "type": "success", "sticky": True, # True/False will display for few seconds if false }, diff --git a/l10n_ar_afipws_fe/models/account_journal_ws.py b/l10n_ar_afipws_fe/models/account_journal_ws.py index a009979b..03a62f26 100644 --- a/l10n_ar_afipws_fe/models/account_journal_ws.py +++ b/l10n_ar_afipws_fe/models/account_journal_ws.py @@ -2,13 +2,17 @@ # For copyright and license notices, see __manifest__.py file in module root # directory ############################################################################## -from markupsafe import Markup -from odoo import models, _ import logging + +from markupsafe import Markup +from odoo import _, models from odoo.exceptions import UserError _logger = logging.getLogger(__name__) +# TODO: unir AccountJournalWs con AccountJournal ya que ambos heredan account.journal +# pylint: disable=R8180 + # Coloco las funciones de WS aqui para limpiar el codigo # de funciones que no ayudan a su lectura @@ -26,21 +30,19 @@ def get_pyafipws_post_invoice_numbers(self): ret = getattr(self, "%s_pyafipws_cuit_document_classes" % afip_ws)(ws) for document_line in ret: - document_type = document_line.split(',') + document_type = document_line.split(",") # call the webservice method to get the last invoice at AFIP: if hasattr(self, "%s_get_pyafipws_last_invoice" % afip_ws): - obj_document_type = type('obj', (object,), {'code': document_type[0]}) - document_type.append(getattr(self, "%s_get_pyafipws_last_invoice" % afip_ws)( - journal_id.l10n_ar_afip_pos_number, obj_document_type, ws - )) + obj_document_type = type("obj", (object,), {"code": document_type[0]}) + document_type.append( + getattr(self, "%s_get_pyafipws_last_invoice" % afip_ws)( + journal_id.l10n_ar_afip_pos_number, obj_document_type, ws + ) + ) else: raise UserError(_("AFIP WS %s not implemented") % afip_ws) - msg.append("%s %05d-%08d" % ( - document_type[1], - int(document_type[0]), - int(document_type[-1]) - )) - journal_id.message_post(body=Markup('
\n').join(msg)) + msg.append("%s %05d-%08d" % (document_type[1], int(document_type[0]), int(document_type[-1]))) + journal_id.message_post(body=Markup("
\n").join(msg)) def get_pyafipws_last_invoice(self, document_type): self.ensure_one() @@ -65,10 +67,7 @@ def get_pyafipws_last_invoice(self, document_type): raise UserError(_("Servicio AFIP Ocupado reintente en unos minutos")) else: raise UserError( - _( - "Hubo un error al conectarse a AFIP, contacte a su" - " proveedor de Odoo para mas información" - ) + _("Hubo un error al conectarse a AFIP, contacte a su" " proveedor de Odoo para mas información") ) def test_pyafipws_point_of_sales(self): @@ -81,9 +80,7 @@ def test_pyafipws_point_of_sales(self): if hasattr(self, "%s_pyafipws_point_of_sales" % afip_ws): ret = getattr(self, "%s_pyafipws_point_of_sales" % afip_ws)(ws) else: - raise UserError( - _("Get point of sale for ws %s is not implemented yet") % (afip_ws) - ) + raise UserError(_("Get point of sale for ws %s is not implemented yet") % (afip_ws)) msg = _(" %s %s") % ( ". ".join(ret), " - ".join([ws.Excepcion, ws.ErrMsg, ws.Obs]), @@ -111,9 +108,7 @@ def get_pyafipws_cuit_document_classes(self): if hasattr(self, "%s_pyafipws_cuit_document_classes" % afip_ws): ret = getattr(self, "%s_pyafipws_cuit_document_classes" % afip_ws)(ws) else: - raise UserError( - _("Get document types for ws %s is not implemented yet") % (afip_ws) - ) + raise UserError(_("Get document types for ws %s is not implemented yet") % (afip_ws)) msg = _("Authorized Document Clases on AFIP\n%s\n. \nObservations: %s") % ( "
".join(ret), ".
".join([ws.Excepcion, ws.ErrMsg, ws.Obs]), @@ -188,22 +183,14 @@ def wsfex_pyafipws_point_of_sales(self, ws): def wsfe_pyafipws_point_of_sales(self, ws): return ws.ParamGetPtosVenta(sep=" ") - def wsfe_get_pyafipws_last_invoice( - self, l10n_ar_afip_pos_number, document_type, ws - ): + def wsfe_get_pyafipws_last_invoice(self, l10n_ar_afip_pos_number, document_type, ws): return ws.CompUltimoAutorizado(document_type.code, l10n_ar_afip_pos_number) - def wsmtxca_get_pyafipws_last_invoice( - self, l10n_ar_afip_pos_number, document_type, ws - ): + def wsmtxca_get_pyafipws_last_invoice(self, l10n_ar_afip_pos_number, document_type, ws): return ws.CompUltimoAutorizado(document_type.code, l10n_ar_afip_pos_number) - def wsfex_get_pyafipws_last_invoice( - self, l10n_ar_afip_pos_number, document_type, ws - ): + def wsfex_get_pyafipws_last_invoice(self, l10n_ar_afip_pos_number, document_type, ws): return ws.GetLastCMP(document_type.code, l10n_ar_afip_pos_number) - def wsbfe_get_pyafipws_last_invoice( - self, l10n_ar_afip_pos_number, document_type, ws - ): + def wsbfe_get_pyafipws_last_invoice(self, l10n_ar_afip_pos_number, document_type, ws): return ws.GetLastCMP(document_type.code, l10n_ar_afip_pos_number) diff --git a/l10n_ar_afipws_fe/models/account_move.py b/l10n_ar_afipws_fe/models/account_move.py index 224fe4c7..632c6d2f 100644 --- a/l10n_ar_afipws_fe/models/account_move.py +++ b/l10n_ar_afipws_fe/models/account_move.py @@ -2,16 +2,18 @@ # For copyright and license notices, see __manifest__.py file in module root # directory ############################################################################## +import base64 import json import logging import sys import traceback from datetime import datetime -from odoo import fields, models, api, _ + +from odoo import _, api, fields, models from odoo.exceptions import UserError from odoo.tools import float_repr -from odoo.addons.l10n_ar_afipws_fe.afip_utils import get_invoice_number_from_response -import base64 + +from . import get_invoice_number_from_response base64.encodestring = base64.encodebytes @@ -35,12 +37,8 @@ class AccountMove(models.Model): copy=False, string="CAE/CAI/CAEA due Date", ) - afip_associated_period_from = fields.Date( - 'AFIP Period from' - ) - afip_associated_period_to = fields.Date( - 'AFIP Period to' - ) + afip_associated_period_from = fields.Date("AFIP Period from") + afip_associated_period_to = fields.Date("AFIP Period to") afip_qr_code = fields.Char(compute="_compute_qr_code", string="AFIP QR code") afip_message = fields.Text( string="AFIP Message", @@ -61,7 +59,6 @@ class AccountMove(models.Model): help="AFIP request result", ) validation_type = fields.Char( - "Validation Type", compute="_compute_validation_type", ) afip_fce_es_anulacion = fields.Boolean( @@ -79,25 +76,29 @@ class AccountMove(models.Model): # super(AccountMove, self - manual_records)._compute_highest_name() def cron_asynchronous_post(self): - queue_limit = self.env['ir.config_parameter'].sudo().get_param('l10n_ar_afipws_fe.queue_limit', 20) - queue = self.search([ - ('asynchronous_post', '=', True), '|', - ('afip_result', '=', False), - ('afip_result', '=', ''), - ], limit=queue_limit) + queue_limit = self.env["ir.config_parameter"].sudo().get_param("l10n_ar_afipws_fe.queue_limit", 20) + queue = self.search( + [ + ("asynchronous_post", "=", True), + "|", + ("afip_result", "=", False), + ("afip_result", "=", ""), + ], + limit=queue_limit, + ) if queue: queue._post() def _get_starting_sequence(self): - """ If use documents then will create a new starting sequence using the document type code prefix and the - journal document number with a 8 padding number """ - if self.journal_id.l10n_latam_use_documents and self.company_id.account_fiscal_country_id.code == "AR" and self.journal_id.afip_ws: + """If use documents then will create a new starting sequence using the document type code prefix and the + journal document number with a 8 padding number""" + if ( + self.journal_id.l10n_latam_use_documents + and self.company_id.account_fiscal_country_id.code == "AR" + and self.journal_id.afip_ws + ): if self.l10n_latam_document_type_id: - number = int( - self.journal_id.get_pyafipws_last_invoice( - self.l10n_latam_document_type_id - ) - ) + number = int(self.journal_id.get_pyafipws_last_invoice(self.l10n_latam_document_type_id)) return self._get_formatted_sequence(number) return super()._get_starting_sequence() @@ -108,26 +109,25 @@ def _set_next_sequence(self): if invoice_number: last_sequence = self._get_formatted_sequence(invoice_number) format, format_values = self._get_sequence_format_param(last_sequence) - format_values['year'] = self[self._sequence_date_field].year % (10 ** format_values['year_length']) - format_values['month'] = self[self._sequence_date_field].month - format_values['seq'] = invoice_number + format_values["year"] = self[self._sequence_date_field].year % (10 ** format_values["year_length"]) + format_values["month"] = self[self._sequence_date_field].month + format_values["seq"] = invoice_number self[self._sequence_field] = format.format(**format_values) return super()._set_next_sequence() - # TODO Esto se deprecaria si la secuencia solo viene de result de afip + # TODO Esto se deprecaria si la secuencia solo viene de result de afip def _get_last_sequence(self, relaxed=False, with_prefix=None): - if self._name == 'account.move' and \ - self.journal_id.l10n_latam_use_documents and \ - self.company_id.account_fiscal_country_id.code == "AR" and \ - not self.afip_auth_code and \ - self.journal_id.afip_ws and self.l10n_latam_document_type_id: - number = int( - self.journal_id.get_pyafipws_last_invoice( - self.l10n_latam_document_type_id - ) - ) + if ( + self._name == "account.move" + and self.journal_id.l10n_latam_use_documents + and self.company_id.account_fiscal_country_id.code == "AR" + and not self.afip_auth_code + and self.journal_id.afip_ws + and self.l10n_latam_document_type_id + ): + number = int(self.journal_id.get_pyafipws_last_invoice(self.l10n_latam_document_type_id)) res = self._get_formatted_sequence(number) else: res = super()._get_last_sequence(relaxed=relaxed, with_prefix=with_prefix) @@ -170,19 +170,12 @@ def _compute_qr_code(self): "tipoCodAut": "E" if rec.afip_auth_mode == "CAE" else "A", "codAut": int(rec.afip_auth_code), } - if ( - len(rec.commercial_partner_id.l10n_latam_identification_type_id) - and rec.commercial_partner_id.vat - ): + if len(rec.commercial_partner_id.l10n_latam_identification_type_id) and rec.commercial_partner_id.vat: qr_dict["tipoDocRec"] = int( rec.commercial_partner_id.l10n_latam_identification_type_id.l10n_ar_afip_code ) - qr_dict["nroDocRec"] = int( - rec.commercial_partner_id.vat.replace("-", "").replace(".", "") - ) - qr_data = base64.encodestring( - json.dumps(qr_dict, indent=None).encode("ascii") - ).decode("ascii") + qr_dict["nroDocRec"] = int(rec.commercial_partner_id.vat.replace("-", "").replace(".", "")) + qr_data = base64.encodestring(json.dumps(qr_dict, indent=None).encode("ascii")).decode("ascii") qr_data = str(qr_data).replace("\n", "") rec.afip_qr_code = "https://www.afip.gob.ar/fe/qr/?p=%s" % qr_data else: @@ -215,10 +208,9 @@ def _post(self, soft=True): def do_pyafipws_request_cae(self): "Request to AFIP the invoices' Authorization Electronic Code (CAE)" - a_invoices = r_invoices = self.env['account.move'] + a_invoices = r_invoices = self.env["account.move"] for inv in self: - afip_ws = inv.journal_id.afip_ws if not afip_ws: continue @@ -277,14 +269,11 @@ def do_pyafipws_request_cae(self): msg = ws.Excepcion else: # avoid encoding problem when raising error - msg = traceback.format_exception_only(sys.exc_type, sys.exc_value)[ - 0 - ] + msg = traceback.format_exception_only(sys.exc_type, sys.exc_value)[0] if msg: _logger.error( - _("AFIP Validation Error. %s" % msg) - + " XML Request: %s XML Response: %s" - % (ws.XmlRequest, ws.XmlResponse) + _("AFIP Validation Error. %s") % msg + + " XML Request: %s XML Response: %s" % (ws.XmlRequest, ws.XmlResponse) ) msg = "\n".join([ws.Obs or "", ws.ErrMsg or ""]) @@ -292,11 +281,11 @@ def do_pyafipws_request_cae(self): r_invoices += inv vals = { - "name": '/', - "afip_result": 'R', - "afip_message": msg, - "afip_xml_request": ws.XmlRequest or '', - "afip_xml_response": ws.XmlResponse or '', + "name": "/", + "afip_result": "R", + "afip_message": msg, + "afip_xml_request": ws.XmlRequest or "", + "afip_xml_response": ws.XmlResponse or "", } inv.sudo().write(vals) inv._cr.commit() @@ -307,18 +296,15 @@ def do_pyafipws_request_cae(self): if hasattr(ws, "FchVencCAE"): vto = datetime.strptime(ws.FchVencCAE, "%Y%m%d").date() - _logger.info( - "CAE solicitado con exito. CAE: %s. Resultado %s" - % (ws.CAE, ws.Resultado) - ) + _logger.info("CAE solicitado con exito. CAE: %s. Resultado %s" % (ws.CAE, ws.Resultado)) vals = { - "afip_auth_mode": "CAE", - "afip_auth_code": ws.CAE, - "afip_auth_code_due": vto, - "afip_result": ws.Resultado, - "afip_message": msg, - "afip_xml_request": ws.XmlRequest, - "afip_xml_response": ws.XmlResponse, + "afip_auth_mode": "CAE", + "afip_auth_code": ws.CAE, + "afip_auth_code_due": vto, + "afip_result": ws.Resultado, + "afip_message": msg, + "afip_xml_request": ws.XmlRequest, + "afip_xml_response": ws.XmlResponse, } inv.sudo().write(vals) diff --git a/l10n_ar_afipws_fe/models/account_move_ws.py b/l10n_ar_afipws_fe/models/account_move_ws.py index 6bd05428..9729d69d 100644 --- a/l10n_ar_afipws_fe/models/account_move_ws.py +++ b/l10n_ar_afipws_fe/models/account_move_ws.py @@ -2,13 +2,17 @@ # For copyright and license notices, see __manifest__.py file in module root # directory ############################################################################## -from odoo import fields, models, api, _ -from odoo.exceptions import UserError -from datetime import datetime import logging +from datetime import datetime + +from odoo import _, fields, models +from odoo.exceptions import UserError _logger = logging.getLogger(__name__) +# TODO: unir AccountMoveWs con AccountMove ya que ambos heredan account.move +# pylint: disable=R8180 + class AccountMove(models.Model): _inherit = "account.move" @@ -23,9 +27,7 @@ def pyafipws_create_invoice(self, ws, invoice_info): if not afip_ws: return if hasattr(self, "%s_pyafipws_create_invoice" % afip_ws): - return getattr(self, "%s_pyafipws_create_invoice" % afip_ws)( - ws, invoice_info - ) + return getattr(self, "%s_pyafipws_create_invoice" % afip_ws)(ws, invoice_info) else: return _("AFIP WS %s not implemented") % afip_ws @@ -102,7 +104,6 @@ def wsfex_pyafipws_create_invoice(self, ws, invoice_info): ) def wsbfe_pyafipws_create_invoice(self, ws, invoice_info): - ws.CrearFactura( invoice_info["tipo_doc"], invoice_info["nro_doc"], @@ -141,11 +142,10 @@ def pyafipws_add_info(self, ws, afip_ws, invoice_info): def pyafipws_add_tax(self, ws): vat_items = self._get_vat() for item in vat_items: - ws.AgregarIva(item['Id'], "%.2f" % item['BaseImp'], "%.2f" % item['Importe']) + ws.AgregarIva(item["Id"], "%.2f" % item["BaseImp"], "%.2f" % item["Importe"]) not_vat_taxes = self.line_ids.filtered( - lambda x: x.tax_line_id - and x.tax_line_id.tax_group_id.l10n_ar_tribute_afip_code + lambda x: x.tax_line_id and x.tax_line_id.tax_group_id.l10n_ar_tribute_afip_code ) for tax in not_vat_taxes: ws.AgregarTributo( @@ -174,9 +174,7 @@ def wsfe_invoice_add_info(self, ws, invoice_info): ws.AgregarOpcional(opcional_id=2101, valor=self.partner_bank_id.acc_number) # agregamos tipo de transmision si esta definido transmission_type = ( - self.env["ir.config_parameter"] - .sudo() - .get_param("l10n_ar_afipws_fe.fce_transmission", "") + self.env["ir.config_parameter"].sudo().get_param("l10n_ar_afipws_fe.fce_transmission", "") ) if transmission_type: ws.AgregarOpcional(opcional_id=27, valor=transmission_type) @@ -197,7 +195,9 @@ def wsfe_invoice_add_info(self, ws, invoice_info): invoice_info["CbteAsoc"].invoice_date.strftime("%Y%m%d"), ) if invoice_info["afip_associated_period_from"] and invoice_info["afip_associated_period_to"]: - ws.AgregarPeriodoComprobantesAsociados(invoice_info["afip_associated_period_from"], invoice_info["afip_associated_period_to"]) + ws.AgregarPeriodoComprobantesAsociados( + invoice_info["afip_associated_period_from"], invoice_info["afip_associated_period_to"] + ) self.pyafipws_add_tax(ws) def wsbfe_invoice_add_info(self, ws, invoice_info): @@ -206,9 +206,7 @@ def wsbfe_invoice_add_info(self, ws, invoice_info): ws.AgregarOpcional(opcional_id=2101, valor=self.partner_bank_id.acc_number) # agregamos tipo de transmision si esta definido transmission_type = ( - self.env["ir.config_parameter"] - .sudo() - .get_param("l10n_ar_afipws_fe.fce_transmission", "") + self.env["ir.config_parameter"].sudo().get_param("l10n_ar_afipws_fe.fce_transmission", "") ) if transmission_type: ws.AgregarOpcional(opcional_id=27, valor=transmission_type) @@ -242,7 +240,6 @@ def wsbfe_invoice_add_info(self, ws, invoice_info): ) def wsfex_invoice_add_info(self, ws, invoice_info): - if invoice_info["CbteAsoc"]: doc_number_parts = self._l10n_ar_get_document_number_parts( invoice_info["CbteAsoc"].l10n_latam_document_number, @@ -267,7 +264,6 @@ def wsfex_invoice_add_info(self, ws, invoice_info): ) def wsmtxca_invoice_add_info(self, ws, invoice_info): - if invoice_info["CbteAsoc"]: doc_number_parts = self._l10n_ar_get_document_number_parts( invoice_info["CbteAsoc"].l10n_latam_document_number, @@ -328,29 +324,18 @@ def base_map_invoice_info(self): invoice_info["pos_number"] = journal.l10n_ar_afip_pos_number invoice_info["doc_afip_code"] = self.l10n_latam_document_type_id.code invoice_info["ws_next_invoice_number"] = ( - int( - self.journal_id.get_pyafipws_last_invoice( - self.l10n_latam_document_type_id - ) - ) - + 1 + int(self.journal_id.get_pyafipws_last_invoice(self.l10n_latam_document_type_id)) + 1 ) invoice_info["partner_id_code"] = invoice_info[ "commercial_partner" ].l10n_latam_identification_type_id.l10n_ar_afip_code invoice_info["tipo_doc"] = invoice_info["partner_id_code"] or "99" - invoice_info["nro_doc"] = ( - invoice_info["partner_id_code"] - and invoice_info["commercial_partner"].vat - or "0" - ) - invoice_info["cbt_desde"] = invoice_info["cbt_hasta"] = invoice_info[ - "cbte_nro" - ] = invoice_info["ws_next_invoice_number"] - invoice_info["concepto"] = invoice_info["tipo_expo"] = int( - self.l10n_ar_afip_concept - ) + invoice_info["nro_doc"] = invoice_info["partner_id_code"] and invoice_info["commercial_partner"].vat or "0" + invoice_info["cbt_desde"] = invoice_info["cbt_hasta"] = invoice_info["cbte_nro"] = invoice_info[ + "ws_next_invoice_number" + ] + invoice_info["concepto"] = invoice_info["tipo_expo"] = int(self.l10n_ar_afip_concept) invoice_info["fecha_cbte"] = self.invoice_date or fields.Date.today() invoice_info["mipyme_fce"] = int(invoice_info["doc_afip_code"]) in [ @@ -404,18 +389,12 @@ def wsfe_map_invoice_info(self): invoice_info = self.base_map_invoice_info() invoice_info["fecha_cbte"] = invoice_info["fecha_cbte"].strftime("%Y%m%d") if invoice_info["fecha_venc_pago"]: - invoice_info["fecha_venc_pago"] = invoice_info["fecha_venc_pago"].strftime( - "%Y%m%d" - ) + invoice_info["fecha_venc_pago"] = invoice_info["fecha_venc_pago"].strftime("%Y%m%d") if invoice_info["fecha_serv_desde"]: - invoice_info["fecha_serv_desde"] = invoice_info[ - "fecha_serv_desde" - ].strftime("%Y%m%d") + invoice_info["fecha_serv_desde"] = invoice_info["fecha_serv_desde"].strftime("%Y%m%d") if invoice_info["fecha_serv_hasta"]: - invoice_info["fecha_serv_hasta"] = invoice_info[ - "fecha_serv_hasta" - ].strftime("%Y%m%d") - if invoice_info["afip_associated_period_from"] and invoice_info["afip_associated_period_to"]: + invoice_info["fecha_serv_hasta"] = invoice_info["fecha_serv_hasta"].strftime("%Y%m%d") + if invoice_info["afip_associated_period_from"] and invoice_info["afip_associated_period_to"]: invoice_info["afip_associated_period_from"] = invoice_info["afip_associated_period_from"].strftime("%Y%m%d") invoice_info["afip_associated_period_to"] = invoice_info["afip_associated_period_to"].strftime("%Y%m%d") @@ -425,17 +404,11 @@ def wsbfe_map_invoice_info(self): invoice_info = self.base_map_invoice_info() invoice_info["fecha_cbte"] = invoice_info["fecha_cbte"].strftime("%Y%m%d") if invoice_info["fecha_venc_pago"]: - invoice_info["fecha_venc_pago"] = invoice_info["fecha_venc_pago"].strftime( - "%Y%m%d" - ) + invoice_info["fecha_venc_pago"] = invoice_info["fecha_venc_pago"].strftime("%Y%m%d") if invoice_info["fecha_serv_desde"]: - invoice_info["fecha_serv_desde"] = invoice_info[ - "fecha_serv_desde" - ].strftime("%Y%m%d") + invoice_info["fecha_serv_desde"] = invoice_info["fecha_serv_desde"].strftime("%Y%m%d") if invoice_info["fecha_serv_hasta"]: - invoice_info["fecha_serv_hasta"] = invoice_info[ - "fecha_serv_hasta" - ].strftime("%Y%m%d") + invoice_info["fecha_serv_hasta"] = invoice_info["fecha_serv_hasta"].strftime("%Y%m%d") if invoice_info["afip_associated_period_from"] and invoice_info["afip_associated_period_to"]: invoice_info["afip_associated_period_from"] = invoice_info["afip_associated_period_from"].strftime("%Y%m%d") @@ -447,8 +420,7 @@ def wsbfe_map_invoice_info(self): invoice_info["imp_iibb"] = invoice_info["amounts"]["iibb_perc_amount"] invoice_info["imp_perc_mun"] = invoice_info["amounts"]["mun_perc_amount"] invoice_info["imp_internos"] = ( - invoice_info["amounts"]["intern_tax_amount"] - + invoice_info["amounts"]["other_taxes_amount"] + invoice_info["amounts"]["intern_tax_amount"] + invoice_info["amounts"]["other_taxes_amount"] ) invoice_info["imp_perc"] = ( invoice_info["amounts"]["vat_perc_amount"] @@ -463,25 +435,14 @@ def wsfex_map_invoice_info(self): invoice_info = self.base_map_invoice_info() country = invoice_info["country"] if not country: - raise UserError( - _( - 'For WS "%s" country is required on partner' - % (self.journal_id.afip_ws) - ) - ) + raise UserError(_('For WS "%s" country is required on partner') % self.journal_id.afip_ws) elif not country.code: raise UserError( - _( - 'For WS "%s" country code is mandatory' - "Country: %s" % (self.journal_id.afip_ws, country.name) - ) + _('For WS "%s" country code is mandatoryCountry: %s') % (self.journal_id.afip_ws, country.name) ) elif not country.l10n_ar_afip_code: raise UserError( - _( - 'For WS "%s" country afip code is mandatory' - "Country: %s" % (self.journal_id.afip_ws, country.name) - ) + _('For WS "%s" country afip code is mandatoryCountry: %s') % (self.journal_id.afip_ws, country.name) ) if invoice_info["afip_associated_period_from"] and invoice_info["afip_associated_period_to"]: invoice_info["afip_associated_period_from"] = invoice_info["afip_associated_period_from"].strftime("%Y%m%d") @@ -497,10 +458,7 @@ def wsfex_map_invoice_info(self): # por lo que verificamos, se pide permiso existente solo # si es tipo expo 1 y es factura (codigo 19), para todo el # resto pasamos cadena vacia - if ( - int(invoice_info["doc_afip_code"]) == 19 - and invoice_info["tipo_expo"] == 1 - ): + if int(invoice_info["doc_afip_code"]) == 19 and invoice_info["tipo_expo"] == 1: # TODO investigar si hay que pasar si ("S") invoice_info["permiso_existente"] = "N" else: @@ -540,20 +498,11 @@ def wsfex_map_invoice_info(self): elif invoice_info["country"].code != "AR" and invoice_info["nro_doc"]: invoice_info["id_impositivo"] = None if self.commercial_partner.is_company: - invoice_info["cuit_pais_cliente"] = invoice_info[ - "country" - ].cuit_juridica + invoice_info["cuit_pais_cliente"] = invoice_info["country"].cuit_juridica else: - invoice_info["cuit_pais_cliente"] = invoice_info[ - "country" - ].cuit_fisica + invoice_info["cuit_pais_cliente"] = invoice_info["country"].cuit_fisica if not invoice_info["cuit_pais_cliente"]: - raise UserError( - _( - "No vat defined for the partner and also no CUIT " - "set on country" - ) - ) + raise UserError(_("No vat defined for the partner and also no CUIT " "set on country")) invoice_info["domicilio_cliente"] = " - ".join( [ @@ -564,9 +513,7 @@ def wsfex_map_invoice_info(self): self.commercial_partner.city or "", ] ) - invoice_info[ - "pais_dst_cmp" - ] = self.commercial_partner.country_id.l10n_ar_afip_code + invoice_info["pais_dst_cmp"] = self.commercial_partner.country_id.l10n_ar_afip_code invoice_info["lines"] = self.invoice_map_info_lines() return invoice_info @@ -575,17 +522,11 @@ def wsmtxca_map_invoice_info(self): invoice_info = self.base_map_invoice_info() invoice_info["fecha_cbte"] = invoice_info["fecha_cbte"].strftime("%Y%m%d") if invoice_info["fecha_venc_pago"]: - invoice_info["fecha_venc_pago"] = invoice_info["fecha_venc_pago"].strftime( - "%Y%m%d" - ) + invoice_info["fecha_venc_pago"] = invoice_info["fecha_venc_pago"].strftime("%Y%m%d") if invoice_info["fecha_serv_desde"]: - invoice_info["fecha_serv_desde"] = invoice_info[ - "fecha_serv_desde" - ].strftime("%Y%m%d") + invoice_info["fecha_serv_desde"] = invoice_info["fecha_serv_desde"].strftime("%Y%m%d") if invoice_info["fecha_serv_hasta"]: - invoice_info["fecha_serv_hasta"] = invoice_info[ - "fecha_serv_hasta" - ].strftime("%Y%m%d") + invoice_info["fecha_serv_hasta"] = invoice_info["fecha_serv_hasta"].strftime("%Y%m%d") invoice_info["obs_generales"] = self.comment invoice_info["lines"] = self.invoice_map_info_lines() return invoice_info @@ -601,9 +542,7 @@ def invoice_map_info_lines(self): if not line.product_uom_id: line_temp["umed"] = "7" elif not line.product_uom_id.l10n_ar_afip_code: - raise UserError( - _("Not afip code con producto UOM %s" % (line.product_uom_id.name)) - ) + raise UserError(_("Not afip code con producto UOM %s") % line.product_uom_id.name) else: line_temp["umed"] = line.product_uom_id.l10n_ar_afip_code # cod_mtx = line.uom_id.l10n_ar_afip_code @@ -613,12 +552,7 @@ def invoice_map_info_lines(self): line_temp["importe"] = line.price_subtotal # calculamos bonificacion haciendo teorico menos importe line_temp["bonif"] = ( - line.discount - and str( - "%.2f" - % (line_temp["precio"] * line_temp["qty"] - line_temp["importe"]) - ) - or None + line.discount and str("%.2f" % (line_temp["precio"] * line_temp["qty"] - line_temp["importe"])) or None ) line_temp["iva_id"] = line.vat_tax_id.tax_group_id.l10n_ar_vat_afip_code vat_taxes_amounts = line.vat_tax_id.compute_all( @@ -628,9 +562,7 @@ def invoice_map_info_lines(self): product=line.product_id, partner=self.partner_id, ) - line_temp["imp_iva"] = sum( - [x["amount"] for x in vat_taxes_amounts["taxes"]] - ) + line_temp["imp_iva"] = sum([x["amount"] for x in vat_taxes_amounts["taxes"]]) lines.append(line_temp) return lines diff --git a/l10n_ar_afipws_fe/models/afipws_connection.py b/l10n_ar_afipws_fe/models/afipws_connection.py index ea36ce13..f5d34009 100644 --- a/l10n_ar_afipws_fe/models/afipws_connection.py +++ b/l10n_ar_afipws_fe/models/afipws_connection.py @@ -2,10 +2,11 @@ # For copyright and license notices, see __manifest__.py file in module root # directory ############################################################################## -from odoo import fields, models, api, _ -from odoo.exceptions import UserError import logging +from odoo import _, api, fields, models +from odoo.exceptions import UserError + _logger = logging.getLogger(__name__) @@ -60,9 +61,7 @@ def _get_ws(self, afip_ws): @api.model def get_afip_ws_url(self, afip_ws, environment_type): - afip_ws_url = super(AfipwsConnection, self).get_afip_ws_url( - afip_ws, environment_type - ) + afip_ws_url = super(AfipwsConnection, self).get_afip_ws_url(afip_ws, environment_type) if afip_ws_url: return afip_ws_url elif afip_ws == "wsfe": diff --git a/l10n_ar_afipws_fe/models/res_config_settings.py b/l10n_ar_afipws_fe/models/res_config_settings.py index 52990891..d9ea3185 100644 --- a/l10n_ar_afipws_fe/models/res_config_settings.py +++ b/l10n_ar_afipws_fe/models/res_config_settings.py @@ -6,7 +6,6 @@ class ResConfigSettings(models.TransientModel): - _inherit = "res.config.settings" l10n_ar_afip_fce_transmission = fields.Selection( diff --git a/l10n_ar_afipws_fe/views/account_move_views.xml b/l10n_ar_afipws_fe/views/account_move_views.xml index 387dfaa7..10e1d895 100644 --- a/l10n_ar_afipws_fe/views/account_move_views.xml +++ b/l10n_ar_afipws_fe/views/account_move_views.xml @@ -31,7 +31,7 @@