diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 00000000..83d1263a --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,320 @@ +# Instrucciones para Copilot – Revisión de código Odoo (v18.0) + +## Contexto + +* El repositorio contiene **módulos Odoo preparados para Odoo 18** (rama `18.0`). +* El objetivo es **revisar cambios de código** y **sugerir mejoras seguras y relevantes**, sin caer en micro-comentarios. + +--- + +## Reglas generales (aplican a todo el código) + +1. **Responder siempre en español.** +2. Detectar y corregir **errores de tipeo u ortografía evidentes** en nombres de variables, métodos o comentarios (cuando sean claros). +3. No sugerir traducciones de docstrings o comentarios entre idiomas (no proponer pasar del inglés al español o viceversa). +4. No proponer agregar docstrings si el método no tiene uno. + + * Si ya existe un docstring, puede sugerirse un estilo básico acorde a PEP8, pero **no será un error** si faltan `return`, tipos o parámetros documentados. +5. No proponer cambios puramente estéticos (espacios, comillas simples vs dobles, orden de imports, etc.). + +--- + +## Revisión de modelos (`models/*.py`) – cuestiones generales + +* Verificar que: + + * Los campos (`fields.*`) tengan nombres claros, consistentes y no entren en conflicto con otros módulos. + * Las relaciones (`Many2one`, `One2many`, `Many2many`) estén bien definidas y referencien modelos válidos, con `ondelete` apropiado. + * Las constraints declaradas con `_sql_constraints` o `@api.constrains` mantengan la integridad esperada y mensajes claros. +* Sugerir uso de `@api.depends` si un campo compute carece de dependencias explícitas. +* Si se redefine un método de Odoo, asegurar que se llama correctamente `super()`, manteniendo el contrato original. +* Si hay lógica nueva, evitar loops costosos con búsquedas dentro de iteraciones; sugerir `mapped`, `filtered`, dominios vectorizados u otras formas más eficientes. + +--- + +## 🧾 Revisión del manifest (`__manifest__.py`) – reglas generales + +* Confirmar que todos los archivos usados (vistas, seguridad, datos, reportes, wizards) estén referenciados en el manifest. +* Verificar dependencias declaradas: que no falten módulos requeridos ni se declaren innecesarios. +* **Regla de versión (obligatoria):** + Siempre que el diff incluya **modificaciones en**: + + * definición de campos o modelos (`models/*.py`, `wizards/*.py`), + * vistas o datos XML (`views/*.xml`, `data/*.xml`, `report/*.xml`, `wizards/*.xml`), + * seguridad (`security/*.csv`, `security/*.xml`), + + **y el `__manifest__.py` no incrementa `version`, sugerir el bump de versión** (por ejemplo, `1.0.0 → 1.0.1`). +* Solo hacerlo una vez por revisión, aunque haya múltiples archivos afectados. + +--- + +## Revisión de vistas XML (`views/*.xml`) – reglas generales + +* Confirmar que se usen herencias (`inherit_id`, `xpath`) en lugar de redefinir vistas completas sin necesidad. +* Validar que los campos referenciados existan en los modelos correspondientes. +* Evitar duplicar gran parte del `arch`; prioriza `xpath` específicos y claros. + +### Notas específicas Odoo 18 (vistas / UI) + +* Las vistas de lista usan el nuevo elemento `` en lugar de ``; si se ve código nuevo en 18 que sigue usando `` para listas estándar, sugiere adaptarlo cuando sea coherente con el resto del módulo. +* Muchas condiciones en vistas pueden escribirse con atributos declarativos (`invisible`, `readonly`, `required`) más simples que combinaciones complejas de `attrs`; sugiere simplificar cuando el diff haga la vista más compleja sin necesidad. + +--- + +## Seguridad y acceso – reglas generales + +* Verificar los archivos `ir.model.access.csv` para nuevos modelos: deben tener permisos mínimos necesarios. +* No proponer abrir acceso global sin justificación. +* Si se agregan nuevos modelos o campos de control de acceso, **recordar el bump de versión** (ver sección de manifest). +* Si se cambian `record rules`, revisar especialmente combinaciones multi-compañía y multi-website. + +### Seguridad y rendimiento del ORM + +* Reforzar las advertencias sobre **SQL crudo**: si el diff muestra `self.env.cr.execute("...%s..." % var)` u otras interpolaciones inseguras, recomendar reemplazarlo por dominios ORM (`search`, `browse`) o, si es inevitable, parametrizar la query para heredar sanitización y reglas de acceso. + * Ejemplo inseguro que debe marcarse: `self.env.cr.execute("SELECT * FROM res_partner WHERE email = '%s'" % email)`. + * Variante segura aceptable: `self.env.cr.execute("SELECT * FROM res_partner WHERE email = %s", (email,))` o, mejor aún, `self.env['res.partner'].search([('email', '=', email)])`. +* Señalar cualquier uso de `eval` o construcción manual de domains a partir de input de usuario (`eval(domain_string)`), proponiendo dominios expresados como listas de tuplas o mediante objetos `Domain`. + * Ejemplo inseguro: `records = self.env['res.partner'].search(eval("[('name','ilike','%s')]" % user_input))`. + * Forma segura: `records = self.env['res.partner'].search([('name', 'ilike', user_input)])`. +* Vigilar patrones ineficientes comunes: bucles que ejecutan `search`/`write` por registro, filtrados manuales tras `search([])` o cómputos que podrían resolverse con `search_count`, `mapped`, `filtered` o `browse` masivo. + * Ejemplo a señalar: `for partner_id in partner_ids: partner = self.env['res.partner'].search([('id', '=', partner_id)])`. + * Proponer `partners = self.env['res.partner'].browse(partner_ids)` y operar sobre el recordset completo. +* Para lecturas planas o exportaciones, preferir `search_fetch(fields=...)` para limitar columnas y reducir memoria. + * Caso ilustrativo: reemplazar listas armadas a mano con `result = self.env['res.partner'].search_fetch(domain=[('is_company', '=', True)], fields=['name', 'email', 'vat'])`. +* Recordar que los writes vectorizados (`recordset.write`) y las operaciones en lotes evitan locks prolongados y mejoran la trazabilidad de auditoría del ORM. + * Ejemplo recomendado: `partners.write({'comment': 'Actualizado masivamente'})` en lugar de iterar y escribir registro por registro. + +--- + +## Cambios estructurales y scripts de migración – **cuestiones generales** + +Cuando el diff sugiera **cambios de estructura de datos**, **siempre evaluar** si corresponde proponer un **script de migración** en `migrations/` (pre/post/end) **y recordar el bump de versión**. + +### Reglas generales de estructura de `migrations/` + +* La carpeta dentro de `migrations/` debe corresponder con la versión declarada en el manifest (p. ej. `migrations/18.0.4.0/`). +* Los scripts deben ser idempotentes, trabajar en lotes y registrar logs claros. + +### Ejemplos de cambios estructurales (actualizado con tus criterios) + +En estos casos **normalmente corresponde** proponer migración (salvo notas en contra): + +1. **Renombrar campos o modelos** + + * **Campos:** proponer migración **solo si el campo es almacenado** en base de datos: + * campos normales (`Char`, `Many2one`, `Boolean`, etc.), + * campos `compute` con `store=True`. + * Campos `compute` **sin** `store=True` no requieren script por el renombre en sí (son virtuales). + * **Modelos:** renombrar modelos **siempre** implica revisar migración (`ir.model`, `ir.model.data`, tablas relacionales, vistas, acciones…). + +2. **Cambiar tipos de campo** + + * Se considera cambio estructural cuando **cambia la representación en la base de datos** (p.ej. `Char → Many2one`, `Selection → Many2one`, `Integer → Monetary`, `Many2one → Many2many`, etc.). + * Cambios “compatibles” a nivel de PostgreSQL **no suelen requerir script**, por ejemplo: + * `Char → Text` o ajustes de tamaño de `Char`; + * cambios de precisión en `Float` sin cambio de semántica. + * Aun así, si el cambio implica lógica nueva (p.ej. pasar de `Boolean` a `Selection` con múltiples estados) puede requerir mapeo de datos. + +3. **Quitar campos para reestructurar información** + + * Por ejemplo, dividir un campo en varios (split) o fusionar varios en uno (merge). + * Siempre revisar si hay datos que deban preservarse antes de eliminar el campo original. + +4. **Agregar campos `compute` almacenados (`store=True`) con backfill** + + * Si el campo nuevo es `compute` y `store=True`, y se espera que tenga valor para **registros históricos**, conviene: + * Proponer **script `post`** que haga el backfill **en lotes**. + * Añadir una **advertencia explícita** cuando el modelo tiene muchos registros (p.ej. millones) para que el cálculo no se haga en una sola transacción que bloquee la tabla. + +5. **Cambiar dominios o valores de campos `selection`** + + * **Añadir nuevos valores de `selection`**: + En general **no requiere migración** si solo se agregan opciones nuevas y no se tocan las existentes. + * **Eliminar o renombrar keys existentes de `selection`**: + * Puede dejar valores históricos huérfanos o inválidos → proponer script que mapee `old_value → new_value` o que normalice registros antiguos. + * Mencionar que hay que tener en cuenta el comportamiento de campos relacionados (p.ej. un `Many2one` con `ondelete` específico) si el `selection` influye en lógica que crea o elimina registros. + * **Cambios de dominio** en campos relacionales (`Many2one`, `Many2many`): + * Si el nuevo dominio excluye valores usados históricamente, puede ser necesario limpiar o remapear datos para que no queden registros en estados imposibles. + * Recordar que el `ondelete` del campo define qué ocurre al eliminar registros apuntados; hay que respetarlo al limpiar datos. + +6. **Cambiar o añadir `_sql_constraints` (unique / index)** + + * Cambios en constraints `UNIQUE` o adición de nuevas constraints/índices pueden **fallar con datos existentes** (duplicados, valores nulos, etc.). + * Al menos, Copilot debe: + * emitir una **advertencia** sobre el riesgo de fallo en el upgrade, + * sugerir revisar datos previos (y, cuando se vea necesario, un **pre-script** que limpie duplicados o normalice datos antes de aplicar la constraint). + +7. **Cambios en `ir.model.data` / XML IDs** + + * Renombres de XML IDs (`module.name → module2.name2`) o cambios en `module` / `name` suelen requerir: + * script para actualizar referencias dependientes (acciones, vistas, menús, records en otros módulos), + * o uso de utilidades de upgrade. + * Caso especial: registros con `no_update="1"`: + * Si cambia solo texto/etiquetas menores, puede no hacer falta migración. + * **Si cambia el contenido lógico** (ej. campo `domain`, configuración, secuencias) y el registro tiene `no_update="1"`, debes **sugerir forzar el cambio**: + * vía script que actualice explícitamente los registros por su `xml_id`, + * o mediante un proceso de “force update” apropiado. + +8. **Cambios de reglas de acceso / propiedad** + + * Cambios profundos en `record rules` o en campos que determinan propiedad (company, website, owner…) pueden necesitar scripts para: + * recomputar propiedad, + * asignar company/website por defecto, + * o migrar datos entre reglas. + +> **Nota:** hemos eliminado explícitamente de esta lista el caso “Añadir `required=True` a campos existentes sin default” como condición automática de migración; Copilot no debe sugerir script de migración **solo** por ese motivo, salvo que en el diff se vea claro que hay datos históricos incompatibles. + +--- + +## Scripts de migración en `migrations/`: pre / post / end (reglas generales) + +> **Objetivo:** preservar datos y mantener instalabilidad/actualizabilidad segura. + +- **pre**: Se ejecutan antes de actualizar el módulo. Útiles para preparar datos o estructuras que eviten fallos durante el upgrade. +- **post**: Se ejecutan justo después de actualizar el módulo. Ideales para recalcular datos, limpiar residuos o ajustar referencias tras el cambio. +- **end**: Se ejecutan al final de la actualización de todos los módulos. Indicados para tareas globales que dependen de múltiples módulos o para ajustes finales. + +### Mapeo de cambio → acción recomendada (actualizado) + +* **Rename de campo almacenado (mismo modelo)** + + * **Pre-script**: crear columna/alias temporal o copiar datos del campo viejo al nuevo antes de que Odoo toque el esquema, si el cambio puede romper constraints. + * **Post-script**: limpieza de residuos, recomputes de campos derivados si aplica. + +* **Renombrar modelo** + + * **Pre-script**: preparar mapeos en `ir.model` y `ir.model.data`, y ajustar referencias técnicas si es necesario. + * **Post-script**: re-enlazar vistas, acciones, menús, reglas y volver a chequear accesos. + +* **Eliminar campo y mover datos a otros campos (split/merge)** + + * **Pre-script**: copiar datos a los nuevos campos (cuando sea posible) antes de que el schema elimine la columna original. + * **Post-script**: normalizar referencias, recalcular computes, limpiar helpers. + +* **Agregar campo `compute` con `store=True`** + + * **Pre-script (opcional y solo en modelos muy grandes)**: crear columna en DB o preparar estructura para evitar locks largos. + * **Post-script (recomendado)**: backfill **en lotes** para poblar el valor almacenado; es importante para modelos con muchos registros. + +* **Cambiar tipo de campo con cambio real de representación** + + * **Pre-script**: crear columna temporal con el nuevo tipo y migrar datos (con conversión). + * **Post-script**: intercambiar/renombrar columnas, borrar la vieja, disparar recomputes si hace falta. + +* **Cambios en `selection` (eliminar/renombrar keys existentes)** + + * **Pre-script**: mapear valores antiguos → nuevos (tabla de mapeo) usando helpers como `change_field_selection_values()` cuando aplique. + * **Post-script**: validar que no quedan valores huérfanos y que las reglas de negocio siguen cumpliéndose. + * **Añadir keys nuevas**: **no proponer script** salvo que el diff muestre una migración masiva explícita de valores. + +* **Nuevas constraints `_sql_constraints` (unique) / índices** + + * **Pre-script (recomendado cuando haya riesgo)**: detectar y resolver duplicados o datos inconsistentes antes de crear la constraint. + * **Post-script**: crear el índice/constraint y, si procede, validar que no haya fallos. + +* **Cambios en registros XML con `no_update="1"`** + + * **Post-script**: actualizar esos registros por API (respetando `xml_id`) cuando el contenido lógico haya cambiado y no vaya a ser reaplicado por el upgrade normal. + +* **Cambios de reglas de acceso / multi-company / multi-website** + + * **Pre- o post-script** según el caso, para rellenar campos obligatorios (company, website, owner) y evitar que registros queden inaccesibles. + +> **Regla general:** si el cambio puede **romper durante el upgrade**, usa **pre-script**; si requiere **recalcular o reaplicar** después del código nuevo, usa **post-script**. Si se necesita una acción global al final, usa **end-script**. + +--- + +## Convenciones de scripts en `migrations/` (generales) + +* Ubicación: `migrations//`. +* Nombres sugeridos: + + * `pre_.py` + * `post_.py` +* Requisitos: + + * Idempotentes (seguros si se ejecutan más de una vez). + * En lotes (`batch_size` razonable) para datasets grandes. + * Logs claros (uso de `_logger.info`). + * Manejo de transacciones cuando aplique (evitar locks largos). + * Documentar al inicio **qué suponen** y **qué garantizan**. + +**Esqueleto mínimo (ejemplo):** + +```python +# migrations//pre_rename_partner_ref.py +from odoo import api, SUPERUSER_ID + +def migrate(cr, registry): + env = api.Environment(cr, SUPERUSER_ID, {}) + partners = env['res.partner'].with_context(active_test=False).search([('old_ref', '!=', False)]) + for batch in range(0, len(partners), 500): + sub = partners[batch:batch+500] + for p in sub: + if not p.new_ref: + p.new_ref = p.old_ref +``` + +```python +# migrations//post_backfill_stored_amount_total.py +from odoo import api, SUPERUSER_ID + +def migrate(cr, registry): + env = api.Environment(cr, SUPERUSER_ID, {}) + Orders = env['sale.order'].with_context(active_test=False) + ids = Orders.search([]).ids + for i in range(0, len(ids), 200): + batch = Orders.browse(ids[i:i+200]) + # Forzar recompute del stored + batch._compute_amount_total() +``` + +--- + +## Checklist rápida para el review (general) + +| Categoría | Qué comprobar Copilot | +| ------------------ | -------------------------------------------------------------------------------------------------------- | +| Modelos | Relaciones válidas; constraints; uso adecuado de `@api.depends`; `super()` correcto | +| Vistas XML | Herencias correctas; campos válidos; adaptación a cambios de versión (p.ej. `` vs ``) | +| Manifest | **Bump de versión obligatorio** si hay cambios en modelos/vistas/seguridad/datos; archivos referenciados | +| Seguridad | Accesos mínimos necesarios; reglas revisadas | +| Migraciones | **Si hay cambios estructurales, sugerir script en `migrations/` (pre/post/end)** y describir qué hace | +| Rendimiento / ORM | Evitar loops costosos; no SQL innecesario; aprovechar las optimizaciones del ORM de la versión | +| Ortografía & typos | Errores evidentes corregibles sin modificar idioma ni estilo | + +--- + +## Heurística práctica para el bump de versión (general) + +* **SI** el diff toca cualquiera de: `models/`, `views/`, `data/`, `report/`, `security/`, `wizards/` + **Y** `__manifest__.py` no cambia `version` → **Sugerir bump**. +* **SI** hay scripts `migrations/pre_*.py` o `migrations/post_*.py` nuevos → **Sugerir al menos minor bump**. +* **SI** hay cambios que rompen compatibilidad (renombres, cambios de tipo con impacto, limpieza masiva de datos) → **Sugerir minor/major** según impacto. + +--- + +## Estilo del feedback (general) + +* Ser breve, claro y útil. Ejemplos: + + * “El campo `partner_id` no se encuentra referenciado en la vista.” + * “Este método redefine `write()` sin usar `super()`.” + * “Tip: hay un error ortográfico en el nombre del parámetro.” + * **Bump + migración:** “Se renombra `old_ref` → `new_ref`: falta **bump de versión** y **pre-script** en `migrations/` para copiar valores antes del upgrade; añadir **post-script** para recompute del stored.” + +* Evitar explicaciones largas o reescrituras completas salvo que el cambio sea claro y necesario. + +--- + +## Resumen operativo para Copilot (v18) + +1. **Detecta cambios en modelos/vistas/seguridad/datos → exige bump de `version` en `__manifest__.py`.** +2. **Si hay cambio estructural (según la lista actualizada) → propone y describe script(s) de migración en `migrations/` (pre/post/end)**, con enfoque idempotente y en lotes. +3. Distingue entre: + + * **cuestiones generales** (válidas para cualquier versión), + * y **matices específicos de Odoo 18** (por ejemplo, uso de ``, passkeys, tours y comportamiento del framework). +4. Mantén el feedback **concreto, breve y accionable**. + +[^odoo18]: Resumen basado en la documentación oficial de Odoo 18 Release Notes y artículos técnicos que analizan sus mejoras de rendimiento y UX. \ No newline at end of file diff --git a/account_background_post/__manifest__.py b/account_background_post/__manifest__.py index 43e06896..66a4a74b 100644 --- a/account_background_post/__manifest__.py +++ b/account_background_post/__manifest__.py @@ -1,6 +1,6 @@ { "name": "Account Background Post", - "version": "18.0.1.0.0", + "version": "18.0.1.1.0", "author": "ADHOC SA", "depends": [ "account", diff --git a/account_background_post/wizards/validate_account_move_views.xml b/account_background_post/wizards/validate_account_move_views.xml index dbff4444..70d28d91 100644 --- a/account_background_post/wizards/validate_account_move_views.xml +++ b/account_background_post/wizards/validate_account_move_views.xml @@ -17,14 +17,16 @@ diff --git a/account_invoice_tax/__manifest__.py b/account_invoice_tax/__manifest__.py index be919652..570b4d23 100644 --- a/account_invoice_tax/__manifest__.py +++ b/account_invoice_tax/__manifest__.py @@ -1,6 +1,6 @@ { "name": "Account Invoice Tax", - "version": "18.0.1.1.0", + "version": "18.0.1.0.0", "author": "ADHOC SA", "category": "Localization", "depends": [ diff --git a/account_invoice_tax/i18n/es.po b/account_invoice_tax/i18n/es.po index d41c037d..e0e547a8 100644 --- a/account_invoice_tax/i18n/es.po +++ b/account_invoice_tax/i18n/es.po @@ -6,15 +6,14 @@ # Juan José Scarafía , 2024 # Rocio Vega, 2025 # Camila Vives, 2025 -# Gonzalo, 2025 # msgid "" msgstr "" "Project-Id-Version: Odoo Server 18.0+e\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-07-22 12:05+0000\n" +"POT-Creation-Date: 2025-10-09 14:37+0000\n" "PO-Revision-Date: 2024-12-23 18:31+0000\n" -"Last-Translator: Gonzalo, 2025\n" +"Last-Translator: Camila Vives, 2025\n" "Language-Team: Spanish (https://app.transifex.com/adhoc/teams/46451/es/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -52,18 +51,13 @@ msgstr "Agregar/Actualizar Impuesto Fijo" msgid "Amount" msgstr "Importe" -#. module: account_invoice_tax -#: model:ir.model.fields,field_description:account_invoice_tax.field_account_invoice_tax_line__amount_company_currency -msgid "Amount Company Currency" -msgstr "Importe moneda de la compañía" - #. module: account_invoice_tax #: model_terms:ir.ui.view,arch_db:account_invoice_tax.view_account_invoice_tax msgid "Cancel" msgstr "Cancelar" #. module: account_invoice_tax -#: model:ir.model.fields,field_description:account_invoice_tax.field_account_invoice_tax__company_id +#: model:ir.model.fields,field_description:account_invoice_tax.field_account_invoice_tax_line__company_id msgid "Company" msgstr "Empresa" @@ -79,6 +73,11 @@ msgstr "Creado por" msgid "Created on" msgstr "Creado el" +#. module: account_invoice_tax +#: model:ir.model.fields,field_description:account_invoice_tax.field_account_invoice_tax_line__currency_id +msgid "Currency" +msgstr "" + #. module: account_invoice_tax #: model:ir.model.fields,field_description:account_invoice_tax.field_account_invoice_tax__display_name #: model:ir.model.fields,field_description:account_invoice_tax.field_account_invoice_tax_line__display_name @@ -92,6 +91,11 @@ msgstr "Nombre para mostrar" msgid "Edit tax lines" msgstr "Editar lineas de impuesto" +#. module: account_invoice_tax +#: model:ir.model.fields,field_description:account_invoice_tax.field_account_invoice_tax_line__existing_tax_ids +msgid "Existing Tax" +msgstr "" + #. module: account_invoice_tax #: model:ir.model.fields,field_description:account_invoice_tax.field_account_invoice_tax__id #: model:ir.model.fields,field_description:account_invoice_tax.field_account_invoice_tax_line__id @@ -103,11 +107,6 @@ msgstr "ID (identificación)" msgid "Invoice Tax" msgstr "Impuesto de Factura" -#. module: account_invoice_tax -#: model:ir.model.fields,field_description:account_invoice_tax.field_account_invoice_tax__is_in_company_currency -msgid "Is In Company Currency" -msgstr "Es en moneda de la compañía" - #. module: account_invoice_tax #: model:ir.model.fields,field_description:account_invoice_tax.field_account_invoice_tax__write_uid #: model:ir.model.fields,field_description:account_invoice_tax.field_account_invoice_tax_line__write_uid diff --git a/account_invoice_tax/wizards/account_invoice_tax.py b/account_invoice_tax/wizards/account_invoice_tax.py index 1f4bef00..5b2d014d 100644 --- a/account_invoice_tax/wizards/account_invoice_tax.py +++ b/account_invoice_tax/wizards/account_invoice_tax.py @@ -1,5 +1,6 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. from odoo import Command, _, api, fields, models +from odoo.exceptions import UserError class AccountInvoiceTax(models.TransientModel): @@ -7,11 +8,8 @@ class AccountInvoiceTax(models.TransientModel): _description = "Account Invoice Tax" move_id = fields.Many2one("account.move", required=True) - company_id = fields.Many2one(related="move_id.company_id") tax_line_ids = fields.One2many("account.invoice.tax_line", "invoice_tax_id") - is_in_company_currency = fields.Boolean(compute="_compute_is_in_company_currency") - @api.model def default_get(self, fields): res = super().default_get(fields) @@ -21,10 +19,14 @@ def default_get(self, fields): else self.env["account.move"] ) res["move_id"] = move_ids[0].id if move_ids else False + if move_ids[0].move_type == "in_invoice": + sign = 1 + else: # For refund + sign = -1 lines = [] for line in move_ids[0].line_ids.filtered(lambda x: x.tax_line_id): lines.append( - Command.create({"tax_id": line.tax_line_id.id, "amount": line.amount_currency, "new_tax": False}) + Command.create({"tax_id": line.tax_line_id.id, "amount": line.amount_currency * sign, "new_tax": False}) ) res["tax_line_ids"] = lines @@ -63,6 +65,11 @@ def action_update_tax(self): # set amount in the new created tax line. En este momento si queda balanceado y se ajusta la linea AP/AR container = {"records": move} + + if move.move_type == "in_invoice": + sign = 1 + else: # For refund + sign = -1 with move._check_balanced(container): with move._sync_dynamic_lines(container): # restauramos todos los valores de impuestos fixed que se habrian recomputado @@ -75,7 +82,7 @@ def action_update_tax(self): for tax_line_id in self.tax_line_ids: # seteamos valor al impuesto segun lo que puso en el wizard line_with_tax = move.line_ids.filtered(lambda x: x.tax_line_id == tax_line_id.tax_id) - line_with_tax.write(tax_line_id._get_amount_updated_values()) + line_with_tax.write({"amount_currency": tax_line_id.amount * sign}) def add_tax_and_new(self): self.add_tax() @@ -88,65 +95,39 @@ def add_tax_and_new(self): "context": self._context, } - @api.depends("move_id") - def _compute_is_in_company_currency(self): - self.is_in_company_currency = self.move_id.currency_id == self.move_id.company_currency_id + @api.constrains("tax_line_ids") + @api.onchange("tax_line_ids") + def check_analytic(self): + taxes = self.tax_line_ids.filtered("tax_id.analytic").mapped("tax_id") + if taxes: + raise UserError( + 'No puede usar este asistente ya que algún impuesto tiene establecido "Incluir en el costo analítico?".\nImpuestos: %s' + % (", ".join(taxes.mapped(lambda x: "%s (%s)" % (x.name, x.id)))) + ) class AccountInvoiceTaxLine(models.TransientModel): _name = "account.invoice.tax_line" _description = "Account Invoice Tax line" + _check_company_auto = True + _check_company_domain = models.check_companies_domain_parent_of invoice_tax_id = fields.Many2one("account.invoice.tax") - tax_id = fields.Many2one("account.tax", required=True) - amount = fields.Float() - amount_company_currency = fields.Float( - compute="_compute_amount_company_currency", - readonly=False, - store=True, + tax_id = fields.Many2one( + "account.tax", + required=True, + check_company=True, + domain="[('type_tax_use', '=', 'purchase'), ('id', 'not in', existing_tax_ids)]", + ) + company_id = fields.Many2one(related="invoice_tax_id.move_id.company_id") + currency_id = fields.Many2one(related="invoice_tax_id.move_id.currency_id") + existing_tax_ids = fields.Many2many("account.tax", compute="_compute_existing_taxes") + amount = fields.Monetary( + currency_field="currency_id", ) - new_tax = fields.Boolean(default=True) - def _get_amount_updated_values(self): - debit = credit = debit_cc = credit_cc = 0 - if self.amount and not self.amount_company_currency: - self._compute_amount_company_currency() - if self.invoice_tax_id.move_id.move_type == "in_invoice": - if self.amount > 0: - debit = self.amount - debit_cc = self.amount_company_currency - elif self.amount < 0: - credit = -self.amount - credit_cc = -self.amount_company_currency - else: # For refund - if self.amount > 0: - credit = self.amount - credit_cc = self.amount_company_currency - elif self.amount < 0: - debit = -self.amount - debit_cc = -self.amount_company_currency - - # If multi currency enable - move_currency = self.invoice_tax_id.move_id.currency_id - company_currency = self.invoice_tax_id.move_id.company_currency_id - not_company_currency = move_currency and move_currency != company_currency - - values = { - "debit": debit_cc if not_company_currency else debit, - "credit": credit_cc if not_company_currency else credit, - "balance": (self.amount_company_currency if not_company_currency else self.amount) * (1 if debit else -1), - } - - if not_company_currency and self.amount: - values["amount_currency"] = self.amount - - return values - - def _compute_amount_company_currency(self): - for line in self: - taxes = line.invoice_tax_id.move_id.tax_totals["subtotals"][0]["tax_groups"] - for tax_group in taxes: - if line.tax_id.id == tax_group["involved_tax_ids"][0]: - line.amount_company_currency = tax_group["tax_amount"] - break + @api.depends("invoice_tax_id.tax_line_ids.tax_id") + def _compute_existing_taxes(self): + for record in self: + record.existing_tax_ids = record.invoice_tax_id.tax_line_ids.mapped("tax_id") diff --git a/account_invoice_tax/wizards/account_invoice_tax_view.xml b/account_invoice_tax/wizards/account_invoice_tax_view.xml index 494ab3ab..3abae574 100644 --- a/account_invoice_tax/wizards/account_invoice_tax_view.xml +++ b/account_invoice_tax/wizards/account_invoice_tax_view.xml @@ -6,14 +6,15 @@
- - - + + + +