# BR-PT-004 - Market Price Import ## Intent Import dated market prices from an `.xlsx` Excel file into `price.price_value`, using `price.price` as the market price index. ## Scope - Wizard: `purchase_trade.import_prices` - Input model: `purchase_trade.import_prices.start` - Result model: `purchase_trade.import_prices.result` - Target models: - `price.price` - `price.price_value` - Menu entry: under `price.menu_price` ## Inputs The wizard reads only the first worksheet of an `.xlsx` file. The first row is treated as the header row. Header names are normalized by lowercasing and removing non-alphanumeric characters, so labels such as `price_index`, `price index`, and `Price Index` map to the same field. Required columns: - `price_index` - `price_date` - `high_price` - `low_price` - `open_price` - `price_value` The wizard options are: - `Create price index if missing` - `Overwrite existing price` ## Date and Numeric Parsing Accepted `price_date` values: - Excel serial date numbers - `YYYY-MM-DD` - `DD/MM/YYYY` - `MM/DD/YYYY` Numeric price fields accept decimal commas or decimal points. Empty numeric cells are imported as empty values. Invalid numeric values are reported as row errors. ## Expected Behavior For each non-empty data row, starting from Excel row 2: 1. Trim and validate `price_index`. 2. Parse `price_date`. 3. Search `price.price` by exact `price_index`. 4. If the price index is missing: - create it when `Create price index if missing` is checked - otherwise skip the row with `price_index missing` 5. Search `price.price_value` by `(price, price_date)`. 6. If an existing price value is found: - update it when `Overwrite existing price` is checked - otherwise skip the row with `price_date already exists` 7. If no existing price value is found, create a new `price.price_value`. ## Created Price Index Defaults When the wizard creates a missing `price.price`, it sets: - `price_index = imported price_index` - `price_desc = imported price_index` - `price_curve_type = future` It also tries to default these references when matching records exist: - `price_type`: `price.fixtype` where `name = Market price` - `price_currency`: `currency.currency` where `code = USD` - `price_calendar`: `price.calendar` where `name = Argus EU` - `price_unit`: `product.uom` where `name = Mt` If the `price_index` contains a `YYYY-MM` style period, the wizard derives a `product.month`: - pattern accepted in the name: `YYYY-MM`, `YYYY/MM`, `YYYY_MM`, `YYYY.MM`, or `YYYY MM` - month name format: `MONYY`, for example `JUL26` - if no matching `product.month` exists, it is created with `is_cotation = True` ## Result Reporting The result screen always shows counts and detail sections for: - created price indexes - imported prices - updated existing prices - skipped records - errors Row-level errors do not stop the whole import; the wizard records the error and continues with the next row. ## Edge Cases - Missing `price_index`: skipped. - Missing `price_date`: skipped. - Invalid `.xlsx` file: blocking `UserError`. - Missing required columns: blocking `UserError`. - Invalid date: row error. - Invalid numeric value: row error. - Existing `(price, price_date)` without overwrite option: skipped. - Existing `(price, price_date)` with overwrite option: updated. - Empty rows are ignored. ## Impacted Files Direct `purchase_trade` files: - `modules/purchase_trade/pricing.py` - `modules/purchase_trade/pricing.xml` - `modules/purchase_trade/view/import_prices_start_form.xml` - `modules/purchase_trade/view/import_prices_result_form.xml` - `modules/purchase_trade/__init__.py` - `modules/purchase_trade/tryton.cfg` - `modules/purchase_trade/tests/test_module.py` External model dependencies: - `modules/price/price.py` - `modules/price/price_value.py` - `modules/price/view/price_value_form.xml` ## Tests Existing focused tests cover: - missing price index skipped when creation is disabled - missing price index created when creation is enabled - default fields on newly created price indexes - period reuse/creation from `price_index` - existing `price_date` skipped when overwrite is disabled - existing `price_date` updated when overwrite is enabled - invalid row values collected as errors - result screen formatting Recommended additional tests: - `.xlsx` header normalization - missing required columns - Excel serial date parsing - empty row ignored - invalid workbook raises `UserError` ## Open Questions - Q: Should `(price, price_date)` be enforced unique at model/database level? - A: Enforce uniqueness at model level for now. Do not add a database constraint yet. - Q: Should created price index defaults remain hardcoded to `Market price`, `USD`, `Argus EU`, and `Mt`? - A: Yes. Keep these defaults hardcoded for this import. - Q: Should ambiguous slash dates prefer `DD/MM/YYYY` over `MM/DD/YYYY`, as currently implemented? - A: Prefer the user's/default locale date format when possible. Fall back to the current order only if no locale preference is available. - Comment: current code tries `DD/MM/YYYY` before `MM/DD/YYYY` and does not inspect locale. Implementing this answer requires a code change. - Q: Should missing `price_value` be allowed, or should it skip/error while high/low/open remain optional? - A: Missing `price_value` is not allowed. Report the row as an error. `high_price`, `low_price`, and `open_price` remain optional. - Comment: current code allows empty `price_value` and imports it as an empty value. Implementing this answer requires a code change. - Q: Should duplicate rows for the same `(price_index, price_date)` inside the same Excel file be treated as an error, skipped after the first row, or resolved by the overwrite option? - A: Report duplicate rows as row errors and do not import or update the duplicate row. - Q: When `Create price index if missing` is enabled, should missing default reference records (`Market price`, `USD`, `Argus EU`, `Mt`) block index creation or remain optional as currently implemented? - A: Remain optional. Create the price index with the reference records that can be found. - Q: Should the import result distinguish business validation errors from technical parsing errors? - A: Yes. Distinguish business validation errors from file, parsing, and technical errors in the import result.