From f6e618c9ed612bb902b44c906ff2780dd367667c Mon Sep 17 00:00:00 2001 From: Julia Elizondo Date: Thu, 16 Jan 2025 14:13:36 -0300 Subject: [PATCH 01/16] Update from template --- .copier-answers.yml | 12 +++ .github/workflows/pre-commit.yml | 34 +++++++ .gitignore | 11 ++- .pre-commit-config.yaml | 69 ++++++++++++++ .vscode/settings.json | 3 + README.md | 17 +--- pyproject.toml | 152 +++++++++++++++++++++++++++++++ 7 files changed, 285 insertions(+), 13 deletions(-) create mode 100644 .copier-answers.yml create mode 100644 .github/workflows/pre-commit.yml create mode 100644 .pre-commit-config.yaml create mode 100644 .vscode/settings.json create mode 100644 pyproject.toml 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..2e0cca51 --- /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..ff5300ef --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "python.languageServer": "None" +} \ No newline at end of file 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/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..9f837a8c --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,152 @@ +[tool.ruff] +line-length = 120 + +[tool.ruff.lint] +select = [ + "E", # pycodestyle errors + "W", # pycodestyle warnings + "C90", # mccabe + "F", # pyflakes + "UP", # pyupgrade + "I", # isort +] +ignore = [ + "UP008", # pyupgrade: Use `super()` instead of `super(__class__, self)` (no autofix) + "UP031", # pyupgrade: use format specifiers instead of percent format (no autofix) + "E712", # avoid equality comparisons to False (no autofix) + "E721", # do not compare types, use 'isinstance()' (no autofix) + "E722", # do not use bare `except` (no autofix) + "E731", # do not assign `lambda` expression, use a `def` (no autofix) + "E741", # ambiguos variable name (no autofix) +] + +[tool.ruff.lint.per-file-ignores] +"__init__.py" = ["F401", "I001"] +"__manifest__.py" = ["B018"] + +[tool.ruff.lint.pycodestyle] +# line-length is set in [tool.ruff], and it's used by the formatter +# in case the formatted can't autofix the line length, it will be reported as an error +# only if it exceeds the max-line-length set here. We use 999 to effectively disable +# this check. +max-line-length = 999 + +[tool.ruff.lint.isort] +combine-as-imports = true +force-wrap-aliases = true +known-third-party = [ + "dateutil", + "git", + "gnupg", + "openupgradelib", + "pkg_resources", + "psycopg2", + "requests", + "setuptools", + "urllib2", + "yaml", +] + +[tool.ruff.lint.mccabe] +max-complexity = 20 + +[tool.pylint.master] +load-plugins = ["pylint_odoo"] +score = false + +[tool.pylint.odoolint] +manifest-required-authors = "ADHOC SA" +manifest-required-keys = ["license"] +manifest-deprecated-keys = ["description", "active"] +license-allowed = [ + "AGPL-3", + "GPL-2", + "GPL-2 or any later version", + "GPL-3", + "GPL-3 or any later version", + "LGPL-3", +] + +[tool.pylint."messages control"] +disable = "all" +enable = [ + "anomalous-backslash-in-string", + "api-one-deprecated", + "api-one-multi-together", + "assignment-from-none", + "attribute-deprecated", + "attribute-string-redundant", + "character-not-valid-in-resource-link", + "class-camelcase", + "consider-merging-classes-inherited", + "context-overridden", + "create-user-wo-reset-password", + "dangerous-default-value", + "dangerous-filter-wo-user", + "dangerous-qweb-replace-wo-priority", + "dangerous-view-replace-wo-priority", + "deprecated-data-xml-node", + "deprecated-openerp-xml-node", + "development-status-allowed", + "duplicate-id-csv", + "duplicate-key", + "duplicate-po-message-definition", + "duplicate-xml-fields", + "duplicate-xml-record-id", + "eval-referenced", + "eval-used", + # "except-pass", # Annoying + "external-request-timeout", + "file-not-used", + "incoherent-interpreter-exec-perm", + "invalid-commit", + "license-allowed", + "manifest-author-string", + "manifest-deprecated-key", + "manifest-maintainers-list", + "manifest-required-author", + "manifest-required-key", + # "manifest-version-format", # Errors on non-migrated modules, and redundant with runbot + "method-compute", + "method-inverse", + "method-required-super", + "method-search", + "missing-newline-extrafiles", + # "missing-return", # Annoying. Not applicable for computed field methods + "odoo-addons-relative-import", + "old-api7-method-defined", + "openerp-exception-warning", + "po-msgstr-variables", + "po-syntax-error", + "pointless-statement", + "pointless-string-statement", + "print-used", + "redundant-keyword-arg", + "redundant-modulename-xml", + "reimported", + "relative-import", + "renamed-field-parameter", + "resource-not-exist", + "return-in-init", + "rst-syntax-error", + "sql-injection", + "str-format-used", + "test-folder-imported", + "too-few-format-args", + "translation-contains-variable", + "translation-field", + # "translation-positional-used", # Annoying in our use case + # "translation-required", # We don't always translate everything, and that's fine + "unnecessary-utf8-coding-comment", + "unreachable", + "use-vim-comment", + "wrong-tabs-instead-of-spaces", + "xml-attribute-translatable", + "xml-deprecated-qweb-directive", + "xml-deprecated-tree-attribute", + "xml-syntax-error" +] + +[tool.pylint.reports] +output-format = "colorized" +msg-template = "{path}:{line}: [{msg_id}({symbol}), {obj}] {msg}" From 865d29d7d9b1655561d1704e46f13cb8e644c726 Mon Sep 17 00:00:00 2001 From: Julia Elizondo Date: Thu, 16 Jan 2025 14:17:38 -0300 Subject: [PATCH 02/16] [IMP] afipws_certificate: attr string redundant --- l10n_ar_afipws/models/afipws_certificate.py | 1 - 1 file changed, 1 deletion(-) diff --git a/l10n_ar_afipws/models/afipws_certificate.py b/l10n_ar_afipws/models/afipws_certificate.py index f260ba52..6c44b7b3 100644 --- a/l10n_ar_afipws/models/afipws_certificate.py +++ b/l10n_ar_afipws/models/afipws_certificate.py @@ -44,7 +44,6 @@ class AfipwsCertificate(models.Model): ("confirmed", "Confirmed"), ("cancel", "Cancelled"), ], - "State", index=True, readonly=True, default="draft", From 4c4676ac7e4ba9cc330e64ef143f2c39bbf46d43 Mon Sep 17 00:00:00 2001 From: Julia Elizondo Date: Thu, 16 Jan 2025 14:19:17 -0300 Subject: [PATCH 03/16] [IMP] afipws_certificate_alias: attr string redundant --- l10n_ar_afipws/models/afipws_certificate_alias.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/l10n_ar_afipws/models/afipws_certificate_alias.py b/l10n_ar_afipws/models/afipws_certificate_alias.py index cc3d1a42..1d198d69 100644 --- a/l10n_ar_afipws/models/afipws_certificate_alias.py +++ b/l10n_ar_afipws/models/afipws_certificate_alias.py @@ -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, From 2e174d44daec044939397ae8a140a0c255d8ae6e Mon Sep 17 00:00:00 2001 From: Julia Elizondo Date: Thu, 16 Jan 2025 14:20:13 -0300 Subject: [PATCH 04/16] [IMP] afipws_connection: attr string redundant --- l10n_ar_afipws/models/afipws_connection.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/l10n_ar_afipws/models/afipws_connection.py b/l10n_ar_afipws/models/afipws_connection.py index 1b5de7ca..c0e990e1 100644 --- a/l10n_ar_afipws/models/afipws_connection.py +++ b/l10n_ar_afipws/models/afipws_connection.py @@ -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( From f0dbcab17f65938da7b101bed15a660745d8e140 Mon Sep 17 00:00:00 2001 From: Julia Elizondo Date: Thu, 16 Jan 2025 14:21:34 -0300 Subject: [PATCH 05/16] [IMP] afipws_connection: translation contains varibale --- l10n_ar_afipws/models/afipws_connection.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/l10n_ar_afipws/models/afipws_connection.py b/l10n_ar_afipws/models/afipws_connection.py index c0e990e1..99d98c7b 100644 --- a/l10n_ar_afipws/models/afipws_connection.py +++ b/l10n_ar_afipws/models/afipws_connection.py @@ -149,7 +149,7 @@ def connect(self): if not ws: raise UserError( - _("AFIP Webservice %s not implemented yet" % (self.afip_ws)) + _('AFIP Webservice %s not implemented yet') % self.afip_ws instead ) # TODO implementar cache y proxy # create the proxy and get the configuration system parameters: From 669e926c7d443bec7fdc62df8fa6c8efa22e4297 Mon Sep 17 00:00:00 2001 From: Julia Elizondo Date: Thu, 16 Jan 2025 14:22:41 -0300 Subject: [PATCH 06/16] [IMP] res_partner_update_form_padron_wizard: attr string redundant --- l10n_ar_afipws/wizard/res_partner_update_from_padron_wizard.py | 2 -- 1 file changed, 2 deletions(-) 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..d4d052bb 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 @@ -82,7 +82,6 @@ def get_fields(self): state = fields.Selection( [("option", "Option"), ("selection", "Selection"), ("finished", "Finished")], - "State", readonly=True, required=True, default="option", @@ -109,7 +108,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, ) From f66576767548bbab831a0a377ebcc6cf58dae497 Mon Sep 17 00:00:00 2001 From: Julia Elizondo Date: Thu, 16 Jan 2025 14:25:20 -0300 Subject: [PATCH 07/16] [IMP] account_move: odoo-addons relative import --- l10n_ar_afipws_fe/models/account_move.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/l10n_ar_afipws_fe/models/account_move.py b/l10n_ar_afipws_fe/models/account_move.py index 224fe4c7..80efd6ab 100644 --- a/l10n_ar_afipws_fe/models/account_move.py +++ b/l10n_ar_afipws_fe/models/account_move.py @@ -10,7 +10,7 @@ from odoo import fields, models, api, _ 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 +from . import get_invoice_number_from_response import base64 base64.encodestring = base64.encodebytes From ae9ee65c9820c0463e7c0295099867ae5d1515fb Mon Sep 17 00:00:00 2001 From: Julia Elizondo Date: Thu, 16 Jan 2025 14:25:56 -0300 Subject: [PATCH 08/16] [IMP] account_move: attr string redundant --- l10n_ar_afipws_fe/models/account_move.py | 1 - 1 file changed, 1 deletion(-) diff --git a/l10n_ar_afipws_fe/models/account_move.py b/l10n_ar_afipws_fe/models/account_move.py index 80efd6ab..128131f4 100644 --- a/l10n_ar_afipws_fe/models/account_move.py +++ b/l10n_ar_afipws_fe/models/account_move.py @@ -61,7 +61,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( From 640bb447fc3e1e7accfc786c8169b65024a825fd Mon Sep 17 00:00:00 2001 From: Julia Elizondo Date: Thu, 16 Jan 2025 14:26:58 -0300 Subject: [PATCH 09/16] [IMP] account_move: translation contains variable --- l10n_ar_afipws_fe/models/account_move.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/l10n_ar_afipws_fe/models/account_move.py b/l10n_ar_afipws_fe/models/account_move.py index 128131f4..7be66bcf 100644 --- a/l10n_ar_afipws_fe/models/account_move.py +++ b/l10n_ar_afipws_fe/models/account_move.py @@ -281,7 +281,7 @@ def do_pyafipws_request_cae(self): ] if msg: _logger.error( - _("AFIP Validation Error. %s" % msg) + _('AFIP Validation Error. %s') % msg + " XML Request: %s XML Response: %s" % (ws.XmlRequest, ws.XmlResponse) ) From a920cf2228f20882090cd7b7f16b4a0f07d4765e Mon Sep 17 00:00:00 2001 From: Julia Elizondo Date: Thu, 16 Jan 2025 14:30:01 -0300 Subject: [PATCH 10/16] [IMP] account_move_ws: translation contains variable --- l10n_ar_afipws_fe/models/account_move_ws.py | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/l10n_ar_afipws_fe/models/account_move_ws.py b/l10n_ar_afipws_fe/models/account_move_ws.py index 6bd05428..c1428d95 100644 --- a/l10n_ar_afipws_fe/models/account_move_ws.py +++ b/l10n_ar_afipws_fe/models/account_move_ws.py @@ -464,24 +464,15 @@ def wsfex_map_invoice_info(self): country = invoice_info["country"] if not country: raise UserError( - _( - 'For WS "%s" country is required on partner' - % (self.journal_id.afip_ws) - ) + _('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") @@ -602,7 +593,7 @@ def invoice_map_info_lines(self): 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)) + _('Not afip code con producto UOM %s') % line.product_uom_id.name ) else: line_temp["umed"] = line.product_uom_id.l10n_ar_afip_code From ce351404104bf2b12e75dac0dfb930d745ec37aa Mon Sep 17 00:00:00 2001 From: Julia Elizondo Date: Thu, 16 Jan 2025 14:32:20 -0300 Subject: [PATCH 11/16] [IMP] account_vat_ledger: attr string redundant --- l10n_ar_reports/models/account_vat_ledger.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/l10n_ar_reports/models/account_vat_ledger.py b/l10n_ar_reports/models/account_vat_ledger.py index e86ac2d3..a780ac69 100644 --- a/l10n_ar_reports/models/account_vat_ledger.py +++ b/l10n_ar_reports/models/account_vat_ledger.py @@ -26,7 +26,7 @@ class AccountVatLedger(models.Model): ), ) type = fields.Selection( - [("sale", "Sale"), ("purchase", "Purchase")], "Type", required=True + [("sale", "Sale"), ("purchase", "Purchase")], required=True ) date_from = fields.Date( string="Start Date", @@ -51,34 +51,28 @@ class AccountVatLedger(models.Model): states={"draft": [("readonly", False)]}, ) first_page = fields.Integer( - "First Page", required=True, readonly=True, states={"draft": [("readonly", False)]}, ) last_page = fields.Integer( - "Last Page", readonly=True, states={"draft": [("readonly", False)]}, ) presented_ledger = fields.Binary( - "Presented Ledger", readonly=True, states={"draft": [("readonly", False)]}, ) presented_ledger_name = fields.Char() state = fields.Selection( [("draft", "Draft"), ("presented", "Presented"), ("cancel", "Cancel")], - "State", required=True, default="draft", ) note = fields.Html("Notes") # Computed fields - name = fields.Char("Titile", compute="_compute_name") - reference = fields.Char( - "Reference", - ) + name = fields.Char("Title", compute="_compute_name") + reference = fields.Char() invoice_ids = fields.Many2many( "account.ar.vat.line", string="Invoices", compute="_compute_invoices" ) From e74289f28bcd074582e7abb0ce7d9ef4dd1e2596 Mon Sep 17 00:00:00 2001 From: Julia Elizondo Date: Mon, 20 Jan 2025 13:15:05 -0300 Subject: [PATCH 12/16] [IMP] l10n_ar_afipws, l10n_ar_afipws_fe: xml deprecated data node --- l10n_ar_afipws/models/afipws_connection.py | 2 +- l10n_ar_afipws/views/res_config_settings.xml | 2 -- l10n_ar_afipws_fe/views/res_config_settings.xml | 2 -- 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/l10n_ar_afipws/models/afipws_connection.py b/l10n_ar_afipws/models/afipws_connection.py index 99d98c7b..b11dd049 100644 --- a/l10n_ar_afipws/models/afipws_connection.py +++ b/l10n_ar_afipws/models/afipws_connection.py @@ -149,7 +149,7 @@ def connect(self): if not ws: raise UserError( - _('AFIP Webservice %s not implemented yet') % self.afip_ws instead + _('AFIP Webservice %s not implemented yet') % self.afip_ws ) # TODO implementar cache y proxy # create the proxy and get the configuration system parameters: 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_fe/views/res_config_settings.xml b/l10n_ar_afipws_fe/views/res_config_settings.xml index 0d7a51d0..ea31db55 100644 --- a/l10n_ar_afipws_fe/views/res_config_settings.xml +++ b/l10n_ar_afipws_fe/views/res_config_settings.xml @@ -1,6 +1,5 @@ - res.config.settings.view.form.inherit.account res.config.settings @@ -13,5 +12,4 @@ - From 9e00d4e6c8762f2115a3c2d52e03b471d72b7605 Mon Sep 17 00:00:00 2001 From: Julia Elizondo Date: Mon, 20 Jan 2025 14:38:06 -0300 Subject: [PATCH 13/16] [IMP] l10n_ar_reports: attr string redundant --- l10n_ar_reports/report/account_ar_vat_line.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/l10n_ar_reports/report/account_ar_vat_line.py b/l10n_ar_reports/report/account_ar_vat_line.py index 0a5f494d..97a3ff09 100644 --- a/l10n_ar_reports/report/account_ar_vat_line.py +++ b/l10n_ar_reports/report/account_ar_vat_line.py @@ -76,7 +76,7 @@ class AccountArVatLine(models.Model): readonly=True, string="Not taxed/ex", currency_field="company_currency_id" ) other_taxes = fields.Monetary( - readonly=True, string="Other Taxes", currency_field="company_currency_id" + readonly=True, currency_field="company_currency_id" ) total = fields.Monetary(readonly=True, currency_field="company_currency_id") state = fields.Selection( From 3302789b7275c509346143737810ddbbae1d4a52 Mon Sep 17 00:00:00 2001 From: Julia Elizondo Date: Mon, 20 Jan 2025 14:45:35 -0300 Subject: [PATCH 14/16] [IMP] l10n_ar_reports: indentation contains spaces and tabs --- l10n_ar_reports/report/account_ar_vat_line.py | 54 +++++++++---------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/l10n_ar_reports/report/account_ar_vat_line.py b/l10n_ar_reports/report/account_ar_vat_line.py index 97a3ff09..6a92430c 100644 --- a/l10n_ar_reports/report/account_ar_vat_line.py +++ b/l10n_ar_reports/report/account_ar_vat_line.py @@ -129,46 +129,46 @@ def init(self): am.state, am.company_id, sum(CASE WHEN btg.l10n_ar_vat_afip_code = '5' AND move_type IN ('out_invoice', 'out_refund') THEN aml.balance*-1 - WHEN btg.l10n_ar_vat_afip_code = '5' AND move_type IN ('in_invoice', 'in_refund') THEN aml.balance - ELSE Null END) as base_21, + WHEN btg.l10n_ar_vat_afip_code = '5' AND move_type IN ('in_invoice', 'in_refund') THEN aml.balance + ELSE Null END) as base_21, sum(CASE WHEN ntg.l10n_ar_vat_afip_code = '5' AND move_type IN ('out_invoice', 'out_refund') THEN aml.balance*-1 - WHEN ntg.l10n_ar_vat_afip_code = '5' AND move_type IN ('in_invoice', 'in_refund') THEN aml.balance - ELSE Null END) as vat_21, + WHEN ntg.l10n_ar_vat_afip_code = '5' AND move_type IN ('in_invoice', 'in_refund') THEN aml.balance + ELSE Null END) as vat_21, sum(CASE WHEN btg.l10n_ar_vat_afip_code = '4' AND move_type IN ('out_invoice', 'out_refund') THEN aml.balance*-1 - WHEN btg.l10n_ar_vat_afip_code = '4' AND move_type IN ('in_invoice', 'in_refund') THEN aml.balance - ELSE Null END) as base_10, + WHEN btg.l10n_ar_vat_afip_code = '4' AND move_type IN ('in_invoice', 'in_refund') THEN aml.balance + ELSE Null END) as base_10, sum(CASE WHEN ntg.l10n_ar_vat_afip_code = '4' AND move_type IN ('out_invoice', 'out_refund') THEN aml.balance*-1 - WHEN ntg.l10n_ar_vat_afip_code = '4' AND move_type IN ('in_invoice', 'in_refund') THEN aml.balance - ELSE Null END) as vat_10, + WHEN ntg.l10n_ar_vat_afip_code = '4' AND move_type IN ('in_invoice', 'in_refund') THEN aml.balance + ELSE Null END) as vat_10, sum(CASE WHEN btg.l10n_ar_vat_afip_code = '6' AND move_type IN ('out_invoice', 'out_refund') THEN aml.balance*-1 - WHEN btg.l10n_ar_vat_afip_code = '6' AND move_type IN ('in_invoice', 'in_refund') THEN aml.balance - ELSE Null END) as base_27, + WHEN btg.l10n_ar_vat_afip_code = '6' AND move_type IN ('in_invoice', 'in_refund') THEN aml.balance + ELSE Null END) as base_27, sum(CASE WHEN ntg.l10n_ar_vat_afip_code = '6' AND move_type IN ('out_invoice', 'out_refund') THEN aml.balance*-1 - WHEN ntg.l10n_ar_vat_afip_code = '6' AND move_type IN ('in_invoice', 'in_refund') THEN aml.balance - ELSE Null END) as vat_27, + WHEN ntg.l10n_ar_vat_afip_code = '6' AND move_type IN ('in_invoice', 'in_refund') THEN aml.balance + ELSE Null END) as vat_27, sum(CASE WHEN btg.l10n_ar_vat_afip_code = '9' AND move_type IN ('out_invoice', 'out_refund') THEN aml.balance*-1 - WHEN btg.l10n_ar_vat_afip_code = '9' AND move_type IN ('in_invoice', 'in_refund') THEN aml.balance - ELSE Null END) as base_25, + WHEN btg.l10n_ar_vat_afip_code = '9' AND move_type IN ('in_invoice', 'in_refund') THEN aml.balance + ELSE Null END) as base_25, sum(CASE WHEN ntg.l10n_ar_vat_afip_code = '9' AND move_type IN ('out_invoice', 'out_refund') THEN aml.balance*-1 - WHEN ntg.l10n_ar_vat_afip_code = '9' AND move_type IN ('in_invoice', 'in_refund') THEN aml.balance - ELSE Null END) as vat_25, + WHEN ntg.l10n_ar_vat_afip_code = '9' AND move_type IN ('in_invoice', 'in_refund') THEN aml.balance + ELSE Null END) as vat_25, sum(CASE WHEN btg.l10n_ar_vat_afip_code = '8' AND move_type IN ('out_invoice', 'out_refund') THEN aml.balance*-1 - WHEN btg.l10n_ar_vat_afip_code = '8' AND move_type IN ('in_invoice', 'in_refund') THEN aml.balance - ELSE Null END) as base_5, + WHEN btg.l10n_ar_vat_afip_code = '8' AND move_type IN ('in_invoice', 'in_refund') THEN aml.balance + ELSE Null END) as base_5, sum(CASE WHEN ntg.l10n_ar_vat_afip_code = '8' AND move_type IN ('out_invoice', 'out_refund') THEN aml.balance*-1 - WHEN ntg.l10n_ar_vat_afip_code = '8' AND move_type IN ('in_invoice', 'in_refund') THEN aml.balance - ELSE Null END) as vat_5, + WHEN ntg.l10n_ar_vat_afip_code = '8' AND move_type IN ('in_invoice', 'in_refund') THEN aml.balance + ELSE Null END) as vat_5, sum(CASE WHEN btg.l10n_ar_vat_afip_code IN ('0', '1', '2', '3', '7') AND move_type IN ('out_invoice', 'out_refund') THEN aml.balance*-1 - WHEN btg.l10n_ar_vat_afip_code IN ('0', '1', '2', '3', '7') AND move_type IN ('in_invoice', 'in_refund') THEN aml.balance - ELSE Null END) as not_taxed, + WHEN btg.l10n_ar_vat_afip_code IN ('0', '1', '2', '3', '7') AND move_type IN ('in_invoice', 'in_refund') THEN aml.balance + ELSE Null END) as not_taxed, sum(CASE WHEN ntg.l10n_ar_tribute_afip_code = '06' AND move_type IN ('out_invoice', 'out_refund') THEN aml.balance*-1 - WHEN ntg.l10n_ar_tribute_afip_code = '06' AND move_type IN ('in_invoice', 'in_refund') THEN aml.balance - ELSE Null END) as vat_per, + WHEN ntg.l10n_ar_tribute_afip_code = '06' AND move_type IN ('in_invoice', 'in_refund') THEN aml.balance + ELSE Null END) as vat_per, sum(CASE WHEN ntg.l10n_ar_vat_afip_code is null and ntg.l10n_ar_tribute_afip_code != '06' AND move_type IN ('out_invoice', 'out_refund') THEN aml.balance*-1 - WHEN ntg.l10n_ar_vat_afip_code is null and ntg.l10n_ar_tribute_afip_code != '06' AND move_type IN ('in_invoice', 'in_refund') THEN aml.balance - ELSE Null END) as other_taxes, + WHEN ntg.l10n_ar_vat_afip_code is null and ntg.l10n_ar_tribute_afip_code != '06' AND move_type IN ('in_invoice', 'in_refund') THEN aml.balance + ELSE Null END) as other_taxes, sum(CASE WHEN move_type IN ('out_invoice', 'out_refund') THEN aml.balance*-1 - WHEN move_type IN ('in_invoice', 'in_refund') THEN aml.balance END) as total + WHEN move_type IN ('in_invoice', 'in_refund') THEN aml.balance END) as total FROM account_move_line aml LEFT JOIN From 63851e620daf18cf09a642eb5c3bfb76f8c7de85 Mon Sep 17 00:00:00 2001 From: Julia Elizondo Date: Mon, 20 Jan 2025 14:53:17 -0300 Subject: [PATCH 15/16] [IMP] account_journal_ws, account_move_ws: bypass qa consider-merging-classes-inherited --- l10n_ar_afipws_fe/models/account_journal_ws.py | 3 +++ l10n_ar_afipws_fe/models/account_move_ws.py | 2 ++ 2 files changed, 5 insertions(+) diff --git a/l10n_ar_afipws_fe/models/account_journal_ws.py b/l10n_ar_afipws_fe/models/account_journal_ws.py index a009979b..a1bdea6e 100644 --- a/l10n_ar_afipws_fe/models/account_journal_ws.py +++ b/l10n_ar_afipws_fe/models/account_journal_ws.py @@ -9,6 +9,9 @@ _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 diff --git a/l10n_ar_afipws_fe/models/account_move_ws.py b/l10n_ar_afipws_fe/models/account_move_ws.py index c1428d95..63fdfdbf 100644 --- a/l10n_ar_afipws_fe/models/account_move_ws.py +++ b/l10n_ar_afipws_fe/models/account_move_ws.py @@ -9,6 +9,8 @@ _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" From 607c81c7d4518fd9ce9e0801c51d2bf3005b1a1c Mon Sep 17 00:00:00 2001 From: Julia Elizondo Date: Mon, 20 Jan 2025 14:54:06 -0300 Subject: [PATCH 16/16] [IMP] *: automatic fixes --- .pre-commit-config.yaml | 2 +- .vscode/settings.json | 2 +- ISSUE_TEMPLATE.md | 16 +- l10n_ar_afipws/__manifest__.py | 2 +- l10n_ar_afipws/models/afipws_certificate.py | 15 +- .../models/afipws_certificate_alias.py | 10 +- l10n_ar_afipws/models/afipws_connection.py | 54 ++----- l10n_ar_afipws/models/res_company.py | 58 +++---- l10n_ar_afipws/models/res_config_settings.py | 1 - l10n_ar_afipws/models/res_partner.py | 32 ++-- .../res_partner_update_from_padron_wizard.py | 15 +- .../wizard/upload_certificate_wizard.py | 3 +- l10n_ar_afipws_fe/__manifest__.py | 2 +- l10n_ar_afipws_fe/afip_utils.py | 5 +- l10n_ar_afipws_fe/models/account_journal.py | 11 +- .../models/account_journal_ws.py | 54 +++---- l10n_ar_afipws_fe/models/account_move.py | 127 +++++++--------- l10n_ar_afipws_fe/models/account_move_ws.py | 141 +++++------------- l10n_ar_afipws_fe/models/afipws_connection.py | 9 +- .../models/res_config_settings.py | 1 - .../views/account_move_views.xml | 2 +- l10n_ar_afipws_fe/views/ir_cron.xml | 2 +- l10n_ar_afipws_fe/wizard/__init__.py | 2 +- .../wizard/account_validate_account_move.py | 18 +-- l10n_ar_pos_afipws_fe/__manifest__.py | 5 +- l10n_ar_pos_afipws_fe/models/pos_order.py | 2 +- l10n_ar_reports/__manifest__.py | 2 +- l10n_ar_reports/models/account_vat_ledger.py | 93 ++++-------- l10n_ar_reports/report/account_ar_vat_line.py | 104 ++++--------- l10n_ar_reports/report/account_vat_xlsx.py | 3 +- requirements.txt | 2 +- 31 files changed, 273 insertions(+), 522 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2e0cca51..0b82f519 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -4,7 +4,7 @@ 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) diff --git a/.vscode/settings.json b/.vscode/settings.json index ff5300ef..277f7c98 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,3 @@ { "python.languageServer": "None" -} \ No newline at end of file +} 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/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 6c44b7b3..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 @@ -97,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 = _( @@ -119,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( @@ -131,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 1d198d69..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: @@ -179,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) @@ -197,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 b11dd049..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" @@ -87,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" @@ -122,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) ) @@ -134,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 @@ -148,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() @@ -167,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/wizard/res_partner_update_from_padron_wizard.py b/l10n_ar_afipws/wizard/res_partner_update_from_padron_wizard.py index d4d052bb..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 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 a1bdea6e..03a62f26 100644 --- a/l10n_ar_afipws_fe/models/account_journal_ws.py +++ b/l10n_ar_afipws_fe/models/account_journal_ws.py @@ -2,9 +2,10 @@ # 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__) @@ -29,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() @@ -68,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): @@ -84,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]), @@ -114,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]), @@ -191,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 7be66bcf..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 . import get_invoice_number_from_response -import base64 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", @@ -78,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() @@ -107,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) @@ -169,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: @@ -214,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 @@ -276,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 ""]) @@ -291,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() @@ -306,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 63fdfdbf..9729d69d 100644 --- a/l10n_ar_afipws_fe/models/account_move_ws.py +++ b/l10n_ar_afipws_fe/models/account_move_ws.py @@ -2,16 +2,18 @@ # 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" @@ -25,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 @@ -104,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"], @@ -143,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( @@ -176,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) @@ -199,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): @@ -208,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) @@ -244,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, @@ -269,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, @@ -330,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 [ @@ -406,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") @@ -427,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") @@ -449,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"] @@ -465,12 +435,10 @@ 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 mandatoryCountry: %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( @@ -490,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: @@ -533,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( [ @@ -557,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 @@ -568,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 @@ -594,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 @@ -606,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( @@ -621,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 @@