Compare commits
No commits in common. "e4e03d4794574e5b01cd654d3ff986916c6cf58f" and "c367e20fc502ef3f0f2feb50187d184bc029ee9c" have entirely different histories.
e4e03d4794
...
c367e20fc5
32 changed files with 3 additions and 1076 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -130,5 +130,3 @@ dmypy.json
|
||||||
|
|
||||||
# Pyre type checker
|
# Pyre type checker
|
||||||
.pyre/
|
.pyre/
|
||||||
|
|
||||||
tmp/
|
|
||||||
|
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 111 KiB |
|
|
@ -8,8 +8,9 @@
|
||||||
# 2. Ejecutar: bash docs/install_logo.sh
|
# 2. Ejecutar: bash docs/install_logo.sh
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
LOGO_SOURCE="/home/snt/Documentos/criptomart/artwork/logo_criptomart_cuadradado_white_background.png"
|
|
||||||
ADDONS=(
|
LOGO_SOURCE="/home/snt/Documentos/lab/odoo/addons-cm/website_sale_aplicoop/static/description/logo_criptomart_cuadradado_white_background.png"
|
||||||
|
ADDtaONS=(
|
||||||
"website_sale_aplicoop"
|
"website_sale_aplicoop"
|
||||||
"product_sale_price_from_pricelist"
|
"product_sale_price_from_pricelist"
|
||||||
"product_pricelist_total_margin"
|
"product_pricelist_total_margin"
|
||||||
|
|
@ -32,7 +33,6 @@ echo "🎨 Instalando logo de CriptoMart en addons..."
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# Copiar logo a cada addon
|
# Copiar logo a cada addon
|
||||||
cd ..
|
|
||||||
for addon in "${ADDONS[@]}"; do
|
for addon in "${ADDONS[@]}"; do
|
||||||
if [ -d "$addon" ]; then
|
if [ -d "$addon" ]; then
|
||||||
# Crear directorio si no existe
|
# Crear directorio si no existe
|
||||||
|
|
|
||||||
|
|
@ -1,155 +0,0 @@
|
||||||
==================
|
|
||||||
Product Origin Text
|
|
||||||
==================
|
|
||||||
|
|
||||||
..
|
|
||||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
|
||||||
!! This file is generated by oca-gen-addon-readme !!
|
|
||||||
!! changes will be overwritten. !!
|
|
||||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
|
||||||
!! source digest: sha256:todo
|
|
||||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
|
||||||
|
|
||||||
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
|
|
||||||
:target: https://odoo-community.org/page/development-status
|
|
||||||
:alt: Beta
|
|
||||||
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
|
|
||||||
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
|
|
||||||
:alt: License: AGPL-3
|
|
||||||
|
|
||||||
|badge1| |badge2|
|
|
||||||
|
|
||||||
This module replaces the structured country/state origin fields from ``product_origin``
|
|
||||||
with a flexible free-text field that can be defined per supplier.
|
|
||||||
|
|
||||||
**Problem:**
|
|
||||||
|
|
||||||
The standard ``product_origin`` module provides structured fields (country + state) for
|
|
||||||
product origin. However, suppliers often use creative and varied formats to describe
|
|
||||||
product origin:
|
|
||||||
|
|
||||||
* "Valencia, Spain"
|
|
||||||
* "Huerta de Ana, Aragón"
|
|
||||||
* "Organic Farm - Northern Italy"
|
|
||||||
* "Local producer - Basque Country"
|
|
||||||
* "Fair Trade - Colombia"
|
|
||||||
|
|
||||||
These free-form descriptions don't fit into structured country/state fields.
|
|
||||||
|
|
||||||
**Solution:**
|
|
||||||
|
|
||||||
This module adds a translatable ``origin_text`` field to ``product.supplierinfo`` that:
|
|
||||||
|
|
||||||
* Allows free-form text to describe product origin
|
|
||||||
* Is stored per supplier (different suppliers may have different origin info)
|
|
||||||
* Is translatable (can be described differently in each language)
|
|
||||||
* Is automatically displayed on the product based on the main vendor
|
|
||||||
(from ``product_main_seller`` module)
|
|
||||||
|
|
||||||
**Features:**
|
|
||||||
|
|
||||||
* Free-text ``Origin`` field in supplier info (Purchase tab of product)
|
|
||||||
* Computed ``Origin`` field in product form (shows main vendor's origin)
|
|
||||||
* Field is visible in product list view (optional column)
|
|
||||||
* Full translation support for multiple languages
|
|
||||||
* Compatible with existing supplier management workflows
|
|
||||||
|
|
||||||
**Table of contents**
|
|
||||||
|
|
||||||
.. contents::
|
|
||||||
:local:
|
|
||||||
|
|
||||||
Installation
|
|
||||||
============
|
|
||||||
|
|
||||||
This module depends on:
|
|
||||||
|
|
||||||
* ``product`` - Base product management
|
|
||||||
* ``product_main_seller`` - To determine the main vendor for products
|
|
||||||
|
|
||||||
To install:
|
|
||||||
|
|
||||||
#. Install the required dependencies
|
|
||||||
#. Install this module through the Odoo Apps menu
|
|
||||||
|
|
||||||
Configuration
|
|
||||||
=============
|
|
||||||
|
|
||||||
No configuration is needed. The module works automatically after installation.
|
|
||||||
|
|
||||||
The origin text displayed on a product is based on the **main vendor** as determined
|
|
||||||
by ``product_main_seller`` (the first supplier in the vendors list).
|
|
||||||
|
|
||||||
Usage
|
|
||||||
=====
|
|
||||||
|
|
||||||
**To add origin information to a product:**
|
|
||||||
|
|
||||||
#. Go to a product form
|
|
||||||
#. Open the **Purchase** tab
|
|
||||||
#. In the **Vendors** section, select a supplier line
|
|
||||||
#. Fill in the **Origin** field with free-form text describing the origin
|
|
||||||
(e.g., "Valencia, Huerta de..., Spain")
|
|
||||||
#. Save the product
|
|
||||||
|
|
||||||
**To view origin information:**
|
|
||||||
|
|
||||||
* The **Origin** field will be automatically displayed on the product form
|
|
||||||
(below the **Main Vendor** field in the Purchase tab)
|
|
||||||
* The origin shown is from the **main vendor** (first supplier in the list)
|
|
||||||
* You can also add the Origin column to product list views
|
|
||||||
|
|
||||||
**To change the displayed origin:**
|
|
||||||
|
|
||||||
* Reorder the vendors list to change which is the main vendor
|
|
||||||
* The origin text will automatically update to show the new main vendor's origin
|
|
||||||
|
|
||||||
**Multi-language support:**
|
|
||||||
|
|
||||||
* The origin text is translatable
|
|
||||||
* Switch to another language and edit the supplier info to provide a translation
|
|
||||||
* Each language can have a different description of the origin
|
|
||||||
|
|
||||||
Bug Tracker
|
|
||||||
===========
|
|
||||||
|
|
||||||
Bugs are tracked on `Criptomart GitLab Issues
|
|
||||||
<https://git.criptomart.net/criptomart/addons-cm/issues>`_.
|
|
||||||
|
|
||||||
In case of trouble, please check there if your issue has already been reported.
|
|
||||||
If you spotted it first, help us to smash it by providing a detailed and welcomed
|
|
||||||
feedback.
|
|
||||||
|
|
||||||
Credits
|
|
||||||
=======
|
|
||||||
|
|
||||||
Contributors
|
|
||||||
------------
|
|
||||||
|
|
||||||
* `Criptomart <https://criptomart.net>`_:
|
|
||||||
|
|
||||||
* Development and implementation
|
|
||||||
|
|
||||||
Financiers
|
|
||||||
----------
|
|
||||||
|
|
||||||
**Development:**
|
|
||||||
|
|
||||||
* `Criptomart <https://criptomart.net>`_
|
|
||||||
|
|
||||||
**Funding:**
|
|
||||||
|
|
||||||
* `Elika Bilbo <https://elikabilbo.eus>`_
|
|
||||||
|
|
||||||
This module was developed by Criptomart with funding from Elika Bilbo.
|
|
||||||
|
|
||||||
Maintainers
|
|
||||||
-----------
|
|
||||||
|
|
||||||
This module is maintained by Criptomart.
|
|
||||||
|
|
||||||
.. image:: https://criptomart.net/logo.png
|
|
||||||
:alt: Criptomart
|
|
||||||
:target: https://criptomart.net
|
|
||||||
|
|
||||||
This module is part of the `addons-cm <https://git.criptomart.net/criptomart/addons-cm>`_ project.
|
|
||||||
|
|
@ -1,114 +0,0 @@
|
||||||
# Product Origin Text - Developer Notes
|
|
||||||
|
|
||||||
## Technical Implementation
|
|
||||||
|
|
||||||
### Architecture
|
|
||||||
|
|
||||||
This module extends the product origin information system to use free-text fields instead of structured country/state fields.
|
|
||||||
|
|
||||||
**Models:**
|
|
||||||
|
|
||||||
1. **product.supplierinfo** - Base model with the `origin_text` field
|
|
||||||
- Field: `origin_text` (Char, translate=True)
|
|
||||||
- This is where the actual data is stored
|
|
||||||
- Each supplier can have a different origin text for the same product
|
|
||||||
|
|
||||||
2. **product.template** - Computed field
|
|
||||||
- Field: `origin_text` (Char, computed, store=False)
|
|
||||||
- Computes from main vendor's supplierinfo
|
|
||||||
- Depends on: `main_seller_id`, `variant_seller_ids.origin_text`
|
|
||||||
|
|
||||||
3. **product.product** - Computed field (variant level)
|
|
||||||
- Field: `origin_text` (Char, computed, store=False)
|
|
||||||
- Computes from main vendor's supplierinfo
|
|
||||||
- Depends on: `product_tmpl_id.main_seller_id`, `seller_ids.origin_text`
|
|
||||||
|
|
||||||
### Why Computed Fields Instead of Related?
|
|
||||||
|
|
||||||
The `origin_text` field in `product.template` and `product.product` cannot be a simple `related` field because:
|
|
||||||
|
|
||||||
- `main_seller_id` is a `res.partner` record
|
|
||||||
- `origin_text` is stored in `product.supplierinfo` records
|
|
||||||
- We need to find the supplierinfo record where `partner_id == main_seller_id`
|
|
||||||
|
|
||||||
This requires a computed field with custom logic to filter and find the correct supplierinfo record.
|
|
||||||
|
|
||||||
### Translation Support
|
|
||||||
|
|
||||||
The `origin_text` field in `product.supplierinfo` uses `translate=True`, which means:
|
|
||||||
|
|
||||||
- Each language can have a different value
|
|
||||||
- Translations are stored in Odoo's translation system
|
|
||||||
- When switching languages, the field shows the translated value
|
|
||||||
- If no translation exists, it falls back to the default language value
|
|
||||||
|
|
||||||
### Store Strategy
|
|
||||||
|
|
||||||
The computed fields in product template and product use `store=False` because:
|
|
||||||
|
|
||||||
- The value should always reflect the current main vendor
|
|
||||||
- If the main vendor changes or its origin text is updated, the product's origin should update automatically
|
|
||||||
- No need to store redundant data
|
|
||||||
- Reduces database size and update complexity
|
|
||||||
|
|
||||||
### Dependencies
|
|
||||||
|
|
||||||
- **product** - Core product module
|
|
||||||
- **product_main_seller** - Provides `main_seller_id` computed field for products
|
|
||||||
|
|
||||||
### Relationship to product_origin
|
|
||||||
|
|
||||||
This module is a replacement/alternative to the OCA `product_origin` module:
|
|
||||||
|
|
||||||
- `product_origin` provides structured fields (country_id, state_id)
|
|
||||||
- `product_origin_char` provides free-text field (origin_text)
|
|
||||||
- Both cannot be installed simultaneously without potential conflicts
|
|
||||||
- This module was created because suppliers use creative, non-standardized origin descriptions
|
|
||||||
|
|
||||||
### Performance Considerations
|
|
||||||
|
|
||||||
- Computed fields with `store=False` are calculated on-the-fly
|
|
||||||
- Performance is acceptable because the computation is simple (filter + get first)
|
|
||||||
- If performance becomes an issue, consider:
|
|
||||||
- Adding `store=True` with proper dependencies
|
|
||||||
- Adding database indexes on frequently searched fields
|
|
||||||
- Caching strategies
|
|
||||||
|
|
||||||
### Testing Strategy
|
|
||||||
|
|
||||||
Tests cover:
|
|
||||||
|
|
||||||
1. **Basic field storage** - Create supplierinfo with origin_text
|
|
||||||
2. **Computed field** - Verify product shows main vendor's origin
|
|
||||||
3. **Main vendor change** - Verify origin updates when main vendor changes
|
|
||||||
4. **Translation** - Verify field is translatable (multilingual support)
|
|
||||||
5. **Empty cases** - Product without vendors, vendor without origin
|
|
||||||
|
|
||||||
### Future Improvements
|
|
||||||
|
|
||||||
Potential enhancements:
|
|
||||||
|
|
||||||
- Add migration script from `product_origin` to convert country/state to text
|
|
||||||
- Add bulk update wizard to set origin for multiple products
|
|
||||||
- Add origin text to purchase order lines
|
|
||||||
- Add search/group by origin in product lists
|
|
||||||
- Add validation rules (max length, format checks)
|
|
||||||
|
|
||||||
## Code Quality
|
|
||||||
|
|
||||||
- Follows OCA guidelines
|
|
||||||
- Black formatted (line length 88)
|
|
||||||
- isort sorted imports
|
|
||||||
- Passes flake8 and pylint checks
|
|
||||||
- Full test coverage
|
|
||||||
- Documented with docstrings
|
|
||||||
- Translatable strings handled correctly (no `_()` in field definitions)
|
|
||||||
|
|
||||||
## Version History
|
|
||||||
|
|
||||||
- **18.0.1.0.0** (2026-02-25) - Initial release
|
|
||||||
- Free-text origin field per supplier
|
|
||||||
- Automatic display based on main vendor
|
|
||||||
- Multi-language support
|
|
||||||
- Full test coverage
|
|
||||||
- OCA documentation structure
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
# Copyright 2026 Criptomart
|
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
|
||||||
|
|
||||||
from . import models # noqa: F401
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
# Copyright 2026 Criptomart
|
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
|
||||||
|
|
||||||
{ # noqa: B018
|
|
||||||
"name": "Product Origin Text",
|
|
||||||
"version": "18.0.1.0.0",
|
|
||||||
"category": "Product",
|
|
||||||
"summary": "Free text origin field per supplier",
|
|
||||||
"author": "Odoo Community Association (OCA), Criptomart",
|
|
||||||
"maintainers": ["Criptomart"],
|
|
||||||
"license": "AGPL-3",
|
|
||||||
"website": "https://git.criptomart.net/criptomart/addons-cm",
|
|
||||||
"depends": [
|
|
||||||
"product",
|
|
||||||
"product_main_seller",
|
|
||||||
],
|
|
||||||
"data": [
|
|
||||||
"views/product_supplierinfo_views.xml",
|
|
||||||
"views/product_template_views.xml",
|
|
||||||
],
|
|
||||||
}
|
|
||||||
|
|
@ -1,50 +0,0 @@
|
||||||
# Translation of Odoo Server.
|
|
||||||
# This file contains the translation of the following modules:
|
|
||||||
# * product_origin_char
|
|
||||||
#
|
|
||||||
msgid ""
|
|
||||||
msgstr ""
|
|
||||||
"Project-Id-Version: Odoo Server 18.0\n"
|
|
||||||
"Report-Msgid-Bugs-To: \n"
|
|
||||||
"POT-Creation-Date: 2026-02-25 12:00+0000\n"
|
|
||||||
"PO-Revision-Date: 2026-02-25 12:00+0000\n"
|
|
||||||
"Last-Translator: \n"
|
|
||||||
"Language-Team: \n"
|
|
||||||
"Language: es\n"
|
|
||||||
"MIME-Version: 1.0\n"
|
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
|
||||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
|
||||||
|
|
||||||
#. module: product_origin_char
|
|
||||||
#: model:ir.model.fields,field_description:product_origin_char.field_product_product__origin_text
|
|
||||||
#: model:ir.model.fields,field_description:product_origin_char.field_product_supplierinfo__origin_text
|
|
||||||
#: model:ir.model.fields,field_description:product_origin_char.field_product_template__origin_text
|
|
||||||
msgid "Origin"
|
|
||||||
msgstr "Origen"
|
|
||||||
|
|
||||||
#. module: product_origin_char
|
|
||||||
#: model:ir.model.fields,help:product_origin_char.field_product_supplierinfo__origin_text
|
|
||||||
msgid "Free text to describe product origin (country, region, producer, etc.)"
|
|
||||||
msgstr "Texto libre para describir el origen del producto (país, región, productor, etc.)"
|
|
||||||
|
|
||||||
#. module: product_origin_char
|
|
||||||
#: model:ir.model.fields,help:product_origin_char.field_product_product__origin_text
|
|
||||||
#: model:ir.model.fields,help:product_origin_char.field_product_template__origin_text
|
|
||||||
msgid "Origin text from main vendor's supplierinfo"
|
|
||||||
msgstr "Texto de origen del proveedor principal"
|
|
||||||
|
|
||||||
#. module: product_origin_char
|
|
||||||
#: model:ir.model,name:product_origin_char.model_product_product
|
|
||||||
msgid "Product"
|
|
||||||
msgstr "Producto"
|
|
||||||
|
|
||||||
#. module: product_origin_char
|
|
||||||
#: model:ir.model,name:product_origin_char.model_product_template
|
|
||||||
msgid "Product Template"
|
|
||||||
msgstr "Plantilla de producto"
|
|
||||||
|
|
||||||
#. module: product_origin_char
|
|
||||||
#: model:ir.model,name:product_origin_char.model_product_supplierinfo
|
|
||||||
msgid "Supplier Pricelist"
|
|
||||||
msgstr "Tarifa de proveedor"
|
|
||||||
|
|
@ -1,50 +0,0 @@
|
||||||
# Translation of Odoo Server.
|
|
||||||
# This file contains the translation of the following modules:
|
|
||||||
# * product_origin_char
|
|
||||||
#
|
|
||||||
msgid ""
|
|
||||||
msgstr ""
|
|
||||||
"Project-Id-Version: Odoo Server 18.0\n"
|
|
||||||
"Report-Msgid-Bugs-To: \n"
|
|
||||||
"POT-Creation-Date: 2026-02-25 12:00+0000\n"
|
|
||||||
"PO-Revision-Date: 2026-02-25 12:00+0000\n"
|
|
||||||
"Last-Translator: \n"
|
|
||||||
"Language-Team: \n"
|
|
||||||
"Language: eu\n"
|
|
||||||
"MIME-Version: 1.0\n"
|
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
|
||||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
|
||||||
|
|
||||||
#. module: product_origin_char
|
|
||||||
#: model:ir.model.fields,field_description:product_origin_char.field_product_product__origin_text
|
|
||||||
#: model:ir.model.fields,field_description:product_origin_char.field_product_supplierinfo__origin_text
|
|
||||||
#: model:ir.model.fields,field_description:product_origin_char.field_product_template__origin_text
|
|
||||||
msgid "Origin"
|
|
||||||
msgstr "Jatorria"
|
|
||||||
|
|
||||||
#. module: product_origin_char
|
|
||||||
#: model:ir.model.fields,help:product_origin_char.field_product_supplierinfo__origin_text
|
|
||||||
msgid "Free text to describe product origin (country, region, producer, etc.)"
|
|
||||||
msgstr "Testu librea produktuaren jatorria deskribatzeko (herrialdea, eskualdea, ekoizlea, etab.)"
|
|
||||||
|
|
||||||
#. module: product_origin_char
|
|
||||||
#: model:ir.model.fields,help:product_origin_char.field_product_product__origin_text
|
|
||||||
#: model:ir.model.fields,help:product_origin_char.field_product_template__origin_text
|
|
||||||
msgid "Origin text from main vendor's supplierinfo"
|
|
||||||
msgstr "Hornitzaile nagusiaren jatorriaren testua"
|
|
||||||
|
|
||||||
#. module: product_origin_char
|
|
||||||
#: model:ir.model,name:product_origin_char.model_product_product
|
|
||||||
msgid "Product"
|
|
||||||
msgstr "Produktua"
|
|
||||||
|
|
||||||
#. module: product_origin_char
|
|
||||||
#: model:ir.model,name:product_origin_char.model_product_template
|
|
||||||
msgid "Product Template"
|
|
||||||
msgstr "Produktu txantiloia"
|
|
||||||
|
|
||||||
#. module: product_origin_char
|
|
||||||
#: model:ir.model,name:product_origin_char.model_product_supplierinfo
|
|
||||||
msgid "Supplier Pricelist"
|
|
||||||
msgstr "Hornitzailearen prezioen zerrenda"
|
|
||||||
|
|
@ -1,49 +0,0 @@
|
||||||
# Translation of Odoo Server.
|
|
||||||
# This file contains the translation of the following modules:
|
|
||||||
# * product_origin_char
|
|
||||||
#
|
|
||||||
msgid ""
|
|
||||||
msgstr ""
|
|
||||||
"Project-Id-Version: Odoo Server 18.0\n"
|
|
||||||
"Report-Msgid-Bugs-To: \n"
|
|
||||||
"POT-Creation-Date: 2026-02-25 12:00+0000\n"
|
|
||||||
"PO-Revision-Date: 2026-02-25 12:00+0000\n"
|
|
||||||
"Last-Translator: \n"
|
|
||||||
"Language-Team: \n"
|
|
||||||
"MIME-Version: 1.0\n"
|
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
|
||||||
"Plural-Forms: \n"
|
|
||||||
|
|
||||||
#. module: product_origin_char
|
|
||||||
#: model:ir.model.fields,field_description:product_origin_char.field_product_product__origin_text
|
|
||||||
#: model:ir.model.fields,field_description:product_origin_char.field_product_supplierinfo__origin_text
|
|
||||||
#: model:ir.model.fields,field_description:product_origin_char.field_product_template__origin_text
|
|
||||||
msgid "Origin"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#. module: product_origin_char
|
|
||||||
#: model:ir.model.fields,help:product_origin_char.field_product_supplierinfo__origin_text
|
|
||||||
msgid "Free text to describe product origin (country, region, producer, etc.)"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#. module: product_origin_char
|
|
||||||
#: model:ir.model.fields,help:product_origin_char.field_product_product__origin_text
|
|
||||||
#: model:ir.model.fields,help:product_origin_char.field_product_template__origin_text
|
|
||||||
msgid "Origin text from main vendor's supplierinfo"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#. module: product_origin_char
|
|
||||||
#: model:ir.model,name:product_origin_char.model_product_product
|
|
||||||
msgid "Product"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#. module: product_origin_char
|
|
||||||
#: model:ir.model,name:product_origin_char.model_product_template
|
|
||||||
msgid "Product Template"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#. module: product_origin_char
|
|
||||||
#: model:ir.model,name:product_origin_char.model_product_supplierinfo
|
|
||||||
msgid "Supplier Pricelist"
|
|
||||||
msgstr ""
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
# Copyright 2026 Criptomart
|
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
|
||||||
|
|
||||||
from . import product_supplierinfo # noqa: F401
|
|
||||||
from . import product_template # noqa: F401
|
|
||||||
from . import product_product # noqa: F401
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
# Copyright 2026 Criptomart
|
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
|
||||||
|
|
||||||
from odoo import api
|
|
||||||
from odoo import fields
|
|
||||||
from odoo import models
|
|
||||||
|
|
||||||
|
|
||||||
class ProductProduct(models.Model):
|
|
||||||
_inherit = "product.product"
|
|
||||||
|
|
||||||
origin_text = fields.Char(
|
|
||||||
string="Origin",
|
|
||||||
compute="_compute_origin_text",
|
|
||||||
store=False,
|
|
||||||
help="Origin text from main vendor's supplierinfo",
|
|
||||||
)
|
|
||||||
|
|
||||||
@api.depends("product_tmpl_id.main_seller_id", "seller_ids.origin_text")
|
|
||||||
def _compute_origin_text(self):
|
|
||||||
for product in self:
|
|
||||||
if product.product_tmpl_id.main_seller_id:
|
|
||||||
# Find the supplierinfo record for the main seller
|
|
||||||
main_seller = product.product_tmpl_id.main_seller_id
|
|
||||||
seller = product.seller_ids.filtered(
|
|
||||||
lambda s, ms=main_seller: s.partner_id == ms
|
|
||||||
)[:1]
|
|
||||||
product.origin_text = seller.origin_text if seller else False
|
|
||||||
else:
|
|
||||||
product.origin_text = False
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
# Copyright 2026 Criptomart
|
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
|
||||||
|
|
||||||
from odoo import fields
|
|
||||||
from odoo import models
|
|
||||||
|
|
||||||
|
|
||||||
class ProductSupplierinfo(models.Model):
|
|
||||||
_inherit = "product.supplierinfo"
|
|
||||||
|
|
||||||
origin_text = fields.Char(
|
|
||||||
string="Origin",
|
|
||||||
translate=True,
|
|
||||||
help="Free text to describe product origin (country, region, producer, etc.)",
|
|
||||||
)
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
# Copyright 2026 Criptomart
|
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
|
||||||
|
|
||||||
from odoo import api
|
|
||||||
from odoo import fields
|
|
||||||
from odoo import models
|
|
||||||
|
|
||||||
|
|
||||||
class ProductTemplate(models.Model):
|
|
||||||
_inherit = "product.template"
|
|
||||||
|
|
||||||
origin_text = fields.Char(
|
|
||||||
string="Origin",
|
|
||||||
compute="_compute_origin_text",
|
|
||||||
store=False,
|
|
||||||
help="Origin text from main vendor's supplierinfo",
|
|
||||||
)
|
|
||||||
|
|
||||||
@api.depends("main_seller_id", "variant_seller_ids.origin_text")
|
|
||||||
def _compute_origin_text(self):
|
|
||||||
for template in self:
|
|
||||||
if template.main_seller_id:
|
|
||||||
# Find the supplierinfo record for the main seller
|
|
||||||
main_seller = template.main_seller_id
|
|
||||||
seller = template.variant_seller_ids.filtered(
|
|
||||||
lambda s, ms=main_seller: s.partner_id == ms
|
|
||||||
)[:1]
|
|
||||||
template.origin_text = seller.origin_text if seller else False
|
|
||||||
else:
|
|
||||||
template.origin_text = False
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
[tool.black]
|
|
||||||
line-length = 88
|
|
||||||
target-version = ['py310']
|
|
||||||
include = '\.pyi?$'
|
|
||||||
exclude = '''
|
|
||||||
/(
|
|
||||||
\.git
|
|
||||||
| \.hg
|
|
||||||
| \.mypy_cache
|
|
||||||
| \.tox
|
|
||||||
| \.venv
|
|
||||||
| _build
|
|
||||||
| buck-out
|
|
||||||
| build
|
|
||||||
| dist
|
|
||||||
| __pycache__
|
|
||||||
)/
|
|
||||||
'''
|
|
||||||
|
|
||||||
[tool.isort]
|
|
||||||
profile = "black"
|
|
||||||
line_length = 88
|
|
||||||
known_odoo = "odoo"
|
|
||||||
known_odoo_addons = "odoo.addons"
|
|
||||||
sections = ["FUTURE", "STDLIB", "THIRDPARTY", "ODOO", "ODOO_ADDONS", "FIRSTPARTY", "LOCALFOLDER"]
|
|
||||||
default_section = "THIRDPARTY"
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
No configuration is needed. The module works automatically after installation.
|
|
||||||
|
|
||||||
The origin text displayed on a product is based on the **main vendor** as determined
|
|
||||||
by ``product_main_seller`` (the first supplier in the vendors list).
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
* `Criptomart <https://criptomart.net>`_:
|
|
||||||
|
|
||||||
* Development and implementation
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
**Development:**
|
|
||||||
|
|
||||||
* `Criptomart <https://criptomart.net>`_
|
|
||||||
|
|
||||||
**Funding:**
|
|
||||||
|
|
||||||
* `Elika Bilbo <https://elikabilbo.eus>`_
|
|
||||||
|
|
||||||
This module was developed by Criptomart with funding from Elika Bilbo.
|
|
||||||
|
|
@ -1,34 +0,0 @@
|
||||||
This module replaces the structured country/state origin fields from ``product_origin``
|
|
||||||
with a flexible free-text field that can be defined per supplier.
|
|
||||||
|
|
||||||
**Problem:**
|
|
||||||
|
|
||||||
The standard ``product_origin`` module provides structured fields (country + state) for
|
|
||||||
product origin. However, suppliers often use creative and varied formats to describe
|
|
||||||
product origin:
|
|
||||||
|
|
||||||
* "Valencia, Spain"
|
|
||||||
* "Huerta de Ana, Aragón"
|
|
||||||
* "Organic Farm - Northern Italy"
|
|
||||||
* "Local producer - Basque Country"
|
|
||||||
* "Fair Trade - Colombia"
|
|
||||||
|
|
||||||
These free-form descriptions don't fit into structured country/state fields.
|
|
||||||
|
|
||||||
**Solution:**
|
|
||||||
|
|
||||||
This module adds a translatable ``origin_text`` field to ``product.supplierinfo`` that:
|
|
||||||
|
|
||||||
* Allows free-form text to describe product origin
|
|
||||||
* Is stored per supplier (different suppliers may have different origin info)
|
|
||||||
* Is translatable (can be described differently in each language)
|
|
||||||
* Is automatically displayed on the product based on the main vendor
|
|
||||||
(from ``product_main_seller`` module)
|
|
||||||
|
|
||||||
**Features:**
|
|
||||||
|
|
||||||
* Free-text ``Origin`` field in supplier info (Purchase tab of product)
|
|
||||||
* Computed ``Origin`` field in product form (shows main vendor's origin)
|
|
||||||
* Field is visible in product list view (optional column)
|
|
||||||
* Full translation support for multiple languages
|
|
||||||
* Compatible with existing supplier management workflows
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
This module depends on:
|
|
||||||
|
|
||||||
* ``product`` - Base product management
|
|
||||||
* ``product_main_seller`` - To determine the main vendor for products
|
|
||||||
|
|
||||||
To install:
|
|
||||||
|
|
||||||
#. Install the required dependencies
|
|
||||||
#. Install this module through the Odoo Apps menu
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
**To add origin information to a product:**
|
|
||||||
|
|
||||||
#. Go to a product form
|
|
||||||
#. Open the **Purchase** tab
|
|
||||||
#. In the **Vendors** section, select a supplier line
|
|
||||||
#. Fill in the **Origin** field with free-form text describing the origin
|
|
||||||
(e.g., "Valencia, Huerta de..., Spain")
|
|
||||||
#. Save the product
|
|
||||||
|
|
||||||
**To view origin information:**
|
|
||||||
|
|
||||||
* The **Origin** field will be automatically displayed on the product form
|
|
||||||
(below the **Main Vendor** field in the Purchase tab)
|
|
||||||
* The origin shown is from the **main vendor** (first supplier in the list)
|
|
||||||
* You can also add the Origin column to product list views
|
|
||||||
|
|
||||||
**To change the displayed origin:**
|
|
||||||
|
|
||||||
* Reorder the vendors list to change which is the main vendor
|
|
||||||
* The origin text will automatically update to show the new main vendor's origin
|
|
||||||
|
|
||||||
**Multi-language support:**
|
|
||||||
|
|
||||||
* The origin text is translatable
|
|
||||||
* Switch to another language and edit the supplier info to provide a translation
|
|
||||||
* Each language can have a different description of the origin
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
Logo placeholder - run install_logo.sh to add CriptoMart logo
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 111 KiB |
|
|
@ -1,123 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
<title>Product Origin Text</title>
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
|
||||||
line-height: 1.6;
|
|
||||||
color: #333;
|
|
||||||
max-width: 900px;
|
|
||||||
margin: 0 auto;
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
h1 {
|
|
||||||
color: #875a7b;
|
|
||||||
border-bottom: 2px solid #875a7b;
|
|
||||||
padding-bottom: 10px;
|
|
||||||
}
|
|
||||||
h2 {
|
|
||||||
color: #875a7b;
|
|
||||||
margin-top: 30px;
|
|
||||||
}
|
|
||||||
.badge {
|
|
||||||
display: inline-block;
|
|
||||||
padding: 3px 7px;
|
|
||||||
border-radius: 3px;
|
|
||||||
font-size: 0.9em;
|
|
||||||
margin-right: 5px;
|
|
||||||
}
|
|
||||||
.badge-beta {
|
|
||||||
background-color: #f0ad4e;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
.badge-license {
|
|
||||||
background-color: #5bc0de;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
ul {
|
|
||||||
padding-left: 20px;
|
|
||||||
}
|
|
||||||
li {
|
|
||||||
margin-bottom: 5px;
|
|
||||||
}
|
|
||||||
code {
|
|
||||||
background-color: #f4f4f4;
|
|
||||||
padding: 2px 5px;
|
|
||||||
border-radius: 3px;
|
|
||||||
font-family: 'Courier New', Courier, monospace;
|
|
||||||
}
|
|
||||||
.feature-box {
|
|
||||||
background-color: #f9f9f9;
|
|
||||||
border-left: 4px solid #875a7b;
|
|
||||||
padding: 15px;
|
|
||||||
margin: 20px 0;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>Product Origin Text</h1>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<span class="badge badge-beta">Beta</span>
|
|
||||||
<span class="badge badge-license">License: AGPL-3</span>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h2>Overview</h2>
|
|
||||||
<p>
|
|
||||||
This module replaces the structured country/state origin fields from
|
|
||||||
<code>product_origin</code> with a flexible free-text field that can be
|
|
||||||
defined per supplier.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div class="feature-box">
|
|
||||||
<h3>Problem:</h3>
|
|
||||||
<p>
|
|
||||||
Suppliers often use creative and varied formats to describe product origin:
|
|
||||||
</p>
|
|
||||||
<ul>
|
|
||||||
<li>"Valencia, Spain"</li>
|
|
||||||
<li>"Huerta de Ana, Aragón"</li>
|
|
||||||
<li>"Organic Farm - Northern Italy"</li>
|
|
||||||
<li>"Local producer - Basque Country"</li>
|
|
||||||
<li>"Fair Trade - Colombia"</li>
|
|
||||||
</ul>
|
|
||||||
<p>
|
|
||||||
These free-form descriptions don't fit into structured country/state fields.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h2>Features</h2>
|
|
||||||
<ul>
|
|
||||||
<li>Free-text <strong>Origin</strong> field in supplier info (Purchase tab of product)</li>
|
|
||||||
<li>Computed <strong>Origin</strong> field in product form (shows main vendor's origin)</li>
|
|
||||||
<li>Field is visible in product list view (optional column)</li>
|
|
||||||
<li>Full translation support for multiple languages</li>
|
|
||||||
<li>Compatible with existing supplier management workflows</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<h2>Usage</h2>
|
|
||||||
<ol>
|
|
||||||
<li>Go to a product form</li>
|
|
||||||
<li>Open the <strong>Purchase</strong> tab</li>
|
|
||||||
<li>In the <strong>Vendors</strong> section, select a supplier line</li>
|
|
||||||
<li>Fill in the <strong>Origin</strong> field with free-form text</li>
|
|
||||||
<li>The origin text from the main vendor will automatically appear on the product</li>
|
|
||||||
</ol>
|
|
||||||
|
|
||||||
<h2>Dependencies</h2>
|
|
||||||
<ul>
|
|
||||||
<li><code>product</code> - Base product management</li>
|
|
||||||
<li><code>product_main_seller</code> - To determine the main vendor for products</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<h2>Credits</h2>
|
|
||||||
<p><strong>Development:</strong> <a href="https://criptomart.net" target="_blank">Criptomart</a></p>
|
|
||||||
<p><strong>Funding:</strong> <a href="https://elikabilbo.eus" target="_blank">Elika Bilbo</a></p>
|
|
||||||
<p style="font-size: 0.9em; color: #666;">
|
|
||||||
This module was developed by Criptomart with funding from Elika Bilbo.
|
|
||||||
</p>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
# Copyright 2026 Criptomart
|
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
|
||||||
|
|
||||||
from . import test_product_origin_char # noqa: F401
|
|
||||||
|
|
@ -1,233 +0,0 @@
|
||||||
# Copyright 2026 Criptomart
|
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
|
||||||
|
|
||||||
from odoo.tests.common import TransactionCase
|
|
||||||
|
|
||||||
|
|
||||||
class TestProductOriginChar(TransactionCase):
|
|
||||||
@classmethod
|
|
||||||
def setUpClass(cls):
|
|
||||||
super().setUpClass()
|
|
||||||
cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True))
|
|
||||||
|
|
||||||
# Create test suppliers
|
|
||||||
cls.supplier_1 = cls.env["res.partner"].create(
|
|
||||||
{
|
|
||||||
"name": "Supplier 1",
|
|
||||||
"is_company": True,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
cls.supplier_2 = cls.env["res.partner"].create(
|
|
||||||
{
|
|
||||||
"name": "Supplier 2",
|
|
||||||
"is_company": True,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
# Create test product
|
|
||||||
cls.product = cls.env["product.product"].create(
|
|
||||||
{
|
|
||||||
"name": "Test Product",
|
|
||||||
"type": "consu",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_01_origin_text_in_supplierinfo(self):
|
|
||||||
"""Test that origin_text can be set in supplierinfo"""
|
|
||||||
supplierinfo = self.env["product.supplierinfo"].create(
|
|
||||||
{
|
|
||||||
"partner_id": self.supplier_1.id,
|
|
||||||
"product_tmpl_id": self.product.product_tmpl_id.id,
|
|
||||||
"origin_text": "Valencia, Spain",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
self.assertEqual(supplierinfo.origin_text, "Valencia, Spain")
|
|
||||||
|
|
||||||
def test_02_origin_from_main_seller(self):
|
|
||||||
"""Test that product shows origin from main seller"""
|
|
||||||
# Create supplierinfo for supplier 1 (will be main seller)
|
|
||||||
self.env["product.supplierinfo"].create(
|
|
||||||
{
|
|
||||||
"partner_id": self.supplier_1.id,
|
|
||||||
"product_tmpl_id": self.product.product_tmpl_id.id,
|
|
||||||
"sequence": 1,
|
|
||||||
"origin_text": "Valencia, Spain",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
# Verify main seller is supplier 1
|
|
||||||
self.product.product_tmpl_id.invalidate_recordset()
|
|
||||||
self.assertEqual(self.product.product_tmpl_id.main_seller_id, self.supplier_1)
|
|
||||||
|
|
||||||
# Verify origin_text on product matches supplier 1's origin
|
|
||||||
self.product.invalidate_recordset()
|
|
||||||
self.assertEqual(self.product.origin_text, "Valencia, Spain")
|
|
||||||
self.assertEqual(self.product.product_tmpl_id.origin_text, "Valencia, Spain")
|
|
||||||
|
|
||||||
def test_03_origin_updates_with_main_seller_change(self):
|
|
||||||
"""Test that origin updates when main seller changes"""
|
|
||||||
# Create supplierinfo for both suppliers
|
|
||||||
supplier1_info = self.env["product.supplierinfo"].create(
|
|
||||||
{
|
|
||||||
"partner_id": self.supplier_1.id,
|
|
||||||
"product_tmpl_id": self.product.product_tmpl_id.id,
|
|
||||||
"sequence": 1,
|
|
||||||
"origin_text": "Valencia, Spain",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
supplier2_info = self.env["product.supplierinfo"].create(
|
|
||||||
{
|
|
||||||
"partner_id": self.supplier_2.id,
|
|
||||||
"product_tmpl_id": self.product.product_tmpl_id.id,
|
|
||||||
"sequence": 2,
|
|
||||||
"origin_text": "Aragón, Spain",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
# Initially, main seller is supplier 1
|
|
||||||
self.product.product_tmpl_id.invalidate_recordset()
|
|
||||||
self.assertEqual(self.product.product_tmpl_id.main_seller_id, self.supplier_1)
|
|
||||||
self.product.invalidate_recordset()
|
|
||||||
self.assertEqual(self.product.origin_text, "Valencia, Spain")
|
|
||||||
|
|
||||||
# Change main seller by swapping sequences
|
|
||||||
supplier1_info.sequence = 2
|
|
||||||
supplier2_info.sequence = 1
|
|
||||||
|
|
||||||
# Verify main seller is now supplier 2
|
|
||||||
self.product.product_tmpl_id.invalidate_recordset()
|
|
||||||
self.assertEqual(self.product.product_tmpl_id.main_seller_id, self.supplier_2)
|
|
||||||
|
|
||||||
# Verify origin_text updated to supplier 2's origin
|
|
||||||
self.product.invalidate_recordset()
|
|
||||||
self.assertEqual(self.product.origin_text, "Aragón, Spain")
|
|
||||||
|
|
||||||
def test_04_empty_origin_without_supplier(self):
|
|
||||||
"""Test that product without suppliers has no origin"""
|
|
||||||
# Create product without suppliers
|
|
||||||
product_no_supplier = self.env["product.product"].create(
|
|
||||||
{
|
|
||||||
"name": "Product Without Supplier",
|
|
||||||
"type": "consu",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
# Verify no main seller and no origin
|
|
||||||
self.assertFalse(product_no_supplier.product_tmpl_id.main_seller_id)
|
|
||||||
self.assertFalse(product_no_supplier.origin_text)
|
|
||||||
|
|
||||||
def test_05_empty_origin_with_supplier_no_text(self):
|
|
||||||
"""Test that supplier without origin_text shows False"""
|
|
||||||
# Create supplierinfo without origin_text
|
|
||||||
self.env["product.supplierinfo"].create(
|
|
||||||
{
|
|
||||||
"partner_id": self.supplier_1.id,
|
|
||||||
"product_tmpl_id": self.product.product_tmpl_id.id,
|
|
||||||
"sequence": 1,
|
|
||||||
# No origin_text set
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
# Verify main seller exists but origin is False
|
|
||||||
self.product.product_tmpl_id.invalidate_recordset()
|
|
||||||
self.assertEqual(self.product.product_tmpl_id.main_seller_id, self.supplier_1)
|
|
||||||
self.product.invalidate_recordset()
|
|
||||||
self.assertFalse(self.product.origin_text)
|
|
||||||
|
|
||||||
def test_06_translation_support(self):
|
|
||||||
"""Test that origin_text field is translatable"""
|
|
||||||
# Create supplierinfo with origin in default language
|
|
||||||
supplierinfo = self.env["product.supplierinfo"].create(
|
|
||||||
{
|
|
||||||
"partner_id": self.supplier_1.id,
|
|
||||||
"product_tmpl_id": self.product.product_tmpl_id.id,
|
|
||||||
"origin_text": "Valencia, Spain",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
# Verify field has translate=True attribute
|
|
||||||
field = self.env["product.supplierinfo"]._fields["origin_text"]
|
|
||||||
self.assertTrue(field.translate)
|
|
||||||
|
|
||||||
# Test that we can set translation (requires lang to be installed)
|
|
||||||
# This is a basic check - full translation testing would require
|
|
||||||
# installing multiple languages
|
|
||||||
self.assertEqual(supplierinfo.origin_text, "Valencia, Spain")
|
|
||||||
|
|
||||||
def test_07_multiple_products_same_supplier(self):
|
|
||||||
"""Test that different products can have different origins from same supplier"""
|
|
||||||
product2 = self.env["product.product"].create(
|
|
||||||
{
|
|
||||||
"name": "Test Product 2",
|
|
||||||
"type": "consu",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
# Create supplierinfo for product 1
|
|
||||||
self.env["product.supplierinfo"].create(
|
|
||||||
{
|
|
||||||
"partner_id": self.supplier_1.id,
|
|
||||||
"product_tmpl_id": self.product.product_tmpl_id.id,
|
|
||||||
"origin_text": "Valencia, Spain",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
# Create supplierinfo for product 2 with same supplier but different origin
|
|
||||||
self.env["product.supplierinfo"].create(
|
|
||||||
{
|
|
||||||
"partner_id": self.supplier_1.id,
|
|
||||||
"product_tmpl_id": product2.product_tmpl_id.id,
|
|
||||||
"origin_text": "Aragón, Spain",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
# Verify each product has its own origin
|
|
||||||
self.product.invalidate_recordset()
|
|
||||||
product2.invalidate_recordset()
|
|
||||||
self.assertEqual(self.product.origin_text, "Valencia, Spain")
|
|
||||||
self.assertEqual(product2.origin_text, "Aragón, Spain")
|
|
||||||
|
|
||||||
def test_08_product_variant_level(self):
|
|
||||||
"""Test that origin_text works at product variant level"""
|
|
||||||
# Create product template with variants
|
|
||||||
product_attr = self.env["product.attribute"].create({"name": "Color"})
|
|
||||||
attr_value_red = self.env["product.attribute.value"].create(
|
|
||||||
{"name": "Red", "attribute_id": product_attr.id}
|
|
||||||
)
|
|
||||||
attr_value_blue = self.env["product.attribute.value"].create(
|
|
||||||
{"name": "Blue", "attribute_id": product_attr.id}
|
|
||||||
)
|
|
||||||
|
|
||||||
product_tmpl = self.env["product.template"].create(
|
|
||||||
{
|
|
||||||
"name": "Product with Variants",
|
|
||||||
"type": "consu",
|
|
||||||
"attribute_line_ids": [
|
|
||||||
(
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
{
|
|
||||||
"attribute_id": product_attr.id,
|
|
||||||
"value_ids": [
|
|
||||||
(6, 0, [attr_value_red.id, attr_value_blue.id])
|
|
||||||
],
|
|
||||||
},
|
|
||||||
)
|
|
||||||
],
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
# Create supplierinfo at template level
|
|
||||||
self.env["product.supplierinfo"].create(
|
|
||||||
{
|
|
||||||
"partner_id": self.supplier_1.id,
|
|
||||||
"product_tmpl_id": product_tmpl.id,
|
|
||||||
"origin_text": "Test Origin",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
# Verify all variants show the same origin (from template level)
|
|
||||||
product_tmpl.invalidate_recordset()
|
|
||||||
for variant in product_tmpl.product_variant_ids:
|
|
||||||
variant.invalidate_recordset()
|
|
||||||
self.assertEqual(variant.origin_text, "Test Origin")
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8" ?>
|
|
||||||
<!-- Copyright 2026 Criptomart
|
|
||||||
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
|
|
||||||
<odoo>
|
|
||||||
<!-- Add origin_text field to product.supplierinfo form view -->
|
|
||||||
<record id="product_supplierinfo_form_view_origin_text" model="ir.ui.view">
|
|
||||||
<field name="name">product.supplierinfo.form.origin.text</field>
|
|
||||||
<field name="model">product.supplierinfo</field>
|
|
||||||
<field name="inherit_id" ref="product.product_supplierinfo_form_view" />
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<field name="product_code" position="after">
|
|
||||||
<field
|
|
||||||
name="origin_text"
|
|
||||||
placeholder="e.g., Valencia, Huerta de..., Spain"
|
|
||||||
/>
|
|
||||||
</field>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<!-- Add origin_text field to supplierinfo tree in product form -->
|
|
||||||
<record id="product_supplierinfo_tree_view_origin_text" model="ir.ui.view">
|
|
||||||
<field name="name">product.supplierinfo.tree.origin.text</field>
|
|
||||||
<field name="model">product.supplierinfo</field>
|
|
||||||
<field name="inherit_id" ref="product.product_supplierinfo_tree_view" />
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<field name="product_code" position="after">
|
|
||||||
<field name="origin_text" optional="show" />
|
|
||||||
</field>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
</odoo>
|
|
||||||
|
|
@ -1,44 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8" ?>
|
|
||||||
<!-- Copyright 2026 Criptomart
|
|
||||||
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
|
|
||||||
<odoo>
|
|
||||||
<!-- Add origin_text field to product template form view -->
|
|
||||||
<record id="product_template_form_view_origin_text" model="ir.ui.view">
|
|
||||||
<field name="name">product.template.form.origin.text</field>
|
|
||||||
<field name="model">product.template</field>
|
|
||||||
<field name="inherit_id" ref="product.product_template_form_view" />
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<group name="purchase" position="inside">
|
|
||||||
<field
|
|
||||||
name="origin_text"
|
|
||||||
readonly="1"
|
|
||||||
string="Origin (from Main Vendor)"
|
|
||||||
/>
|
|
||||||
</group>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<!-- Add origin_text field to product product tree view -->
|
|
||||||
<record id="product_product_tree_view_origin_text" model="ir.ui.view">
|
|
||||||
<field name="name">product.product.tree.origin.text</field>
|
|
||||||
<field name="model">product.product</field>
|
|
||||||
<field name="inherit_id" ref="product.product_product_tree_view" />
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<field name="default_code" position="after">
|
|
||||||
<field name="origin_text" optional="hide" />
|
|
||||||
</field>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
<!-- Add origin_text field to product template tree view -->
|
|
||||||
<record id="product_template_tree_view_origin_text" model="ir.ui.view">
|
|
||||||
<field name="name">product.template.tree.origin.text</field>
|
|
||||||
<field name="model">product.template</field>
|
|
||||||
<field name="inherit_id" ref="product_main_seller.view_product_template_tree" />
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<field name="main_seller_id" position="after">
|
|
||||||
<field name="origin_text" optional="hide" />
|
|
||||||
</field>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
</odoo>
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 111 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 111 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 111 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 111 KiB |
Loading…
Add table
Add a link
Reference in a new issue