add purchase_order_product_recommendation_supermarket
This commit is contained in:
parent
7b37ed92a1
commit
e2a5a830f3
8 changed files with 558 additions and 0 deletions
30
purchase_order_product_recommendation_supermarket/README.rst
Normal file
30
purchase_order_product_recommendation_supermarket/README.rst
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
=================================================
|
||||||
|
Purchase Order Product Recommendation Supermarket
|
||||||
|
=================================================
|
||||||
|
|
||||||
|
Extends “purchase_order_product_recommendation” to add functionalities for food shops
|
||||||
|
|
||||||
|
Purpose
|
||||||
|
=======
|
||||||
|
|
||||||
|
#. Allows to indicate the number of days for which the quantity calculation will be made. If this field is defined, the quantity ordered will be calculated as “quantity/day * no. of days to cover”.
|
||||||
|
#. Allows to define the quantities according to the packaging of the products.
|
||||||
|
#. New check field “Do not take into account days with 0 stock”. In the case of activating this check, the desired behavior would be to disregard in the calculation of daily sales the days when the stock of that product was at 0 or less than 0.
|
||||||
|
#. New column indicating the scraps of the product in the period under analysis
|
||||||
|
|
||||||
|
Configuration
|
||||||
|
=============
|
||||||
|
|
||||||
|
To configure this module, you need to:
|
||||||
|
|
||||||
|
#. Mark the desired settings in the wizard
|
||||||
|
|
||||||
|
Usage
|
||||||
|
=====
|
||||||
|
|
||||||
|
To use this module, you need to:
|
||||||
|
|
||||||
|
#. Go to the purchase order form view.
|
||||||
|
#. Click on the "Recommend Products" button.
|
||||||
|
#. In the wizard that appears, configure the desired settings.
|
||||||
|
#. Click "Confirm" to generate the product recommendations.
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
from . import wizards
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
# Copyright 2025 Criptomart
|
||||||
|
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "Purchase Order Product Recommendation Supermarket",
|
||||||
|
"summary": """Extends “purchase_order_product_recommendation” to add functionalities for food shops""",
|
||||||
|
"version": "16.0.0.1.0",
|
||||||
|
"license": "AGPL-3",
|
||||||
|
"author": "Criptomart",
|
||||||
|
"website": "https://criptomart.net",
|
||||||
|
"depends": ["purchase_order_product_recommendation"],
|
||||||
|
"data": ["wizards/purchase_order_recommendation.xml"],
|
||||||
|
"demo": [],
|
||||||
|
}
|
||||||
138
purchase_order_product_recommendation_supermarket/i18n/es.po
Normal file
138
purchase_order_product_recommendation_supermarket/i18n/es.po
Normal file
|
|
@ -0,0 +1,138 @@
|
||||||
|
# Translation of Odoo Server.
|
||||||
|
# This file contains the translation of the following modules:
|
||||||
|
# * purchase_order_product_recommendation_supermarket
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: Odoo Server 16.0\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2025-06-10 15:20+0000\n"
|
||||||
|
"PO-Revision-Date: 2025-06-10 17:33+0200\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"
|
||||||
|
"X-Generator: Poedit 3.6\n"
|
||||||
|
|
||||||
|
#. module: purchase_order_product_recommendation_supermarket
|
||||||
|
#: model:ir.model.fields,field_description:purchase_order_product_recommendation_supermarket.field_purchase_order_recommendation__avg_days_between_orders
|
||||||
|
msgid "Average days between orders"
|
||||||
|
msgstr "Promedio de días entre pedidos"
|
||||||
|
|
||||||
|
#. module: purchase_order_product_recommendation_supermarket
|
||||||
|
#: model:ir.model.fields,help:purchase_order_product_recommendation_supermarket.field_purchase_order_recommendation__avg_days_between_orders
|
||||||
|
msgid "Average number of days between orders for this vendor."
|
||||||
|
msgstr "Número medio de días entre pedidos para este proveedor."
|
||||||
|
|
||||||
|
#. module: purchase_order_product_recommendation_supermarket
|
||||||
|
#: model:ir.model.fields,field_description:purchase_order_product_recommendation_supermarket.field_purchase_order_recommendation_line__days_without_stock
|
||||||
|
msgid "Days without stock"
|
||||||
|
msgstr "Días sin existencias"
|
||||||
|
|
||||||
|
#. module: purchase_order_product_recommendation_supermarket
|
||||||
|
#: model:ir.model.fields,help:purchase_order_product_recommendation_supermarket.field_purchase_order_recommendation__ignore_zero_stock_days
|
||||||
|
msgid ""
|
||||||
|
"If enabled, days when the product stock was 0 or less will not be "
|
||||||
|
"considered in the daily sales calculation."
|
||||||
|
msgstr ""
|
||||||
|
"Si se activa, los días en los que el stock del producto era 0 o negativo no "
|
||||||
|
"se tendrán en cuenta en el cálculo de las ventas diarias."
|
||||||
|
|
||||||
|
#. module: purchase_order_product_recommendation_supermarket
|
||||||
|
#: model:ir.model.fields,field_description:purchase_order_product_recommendation_supermarket.field_purchase_order_recommendation__ignore_zero_stock_days
|
||||||
|
msgid "Ignore days with zero stock"
|
||||||
|
msgstr "Ignorar los días sin existencias"
|
||||||
|
|
||||||
|
#. module: purchase_order_product_recommendation_supermarket
|
||||||
|
#: model:ir.model.fields,help:purchase_order_product_recommendation_supermarket.field_purchase_order_recommendation__order_days
|
||||||
|
msgid ""
|
||||||
|
"Indicate for how many days the new order should cover the stock. If not "
|
||||||
|
"set, the default module behavior is kept."
|
||||||
|
msgstr ""
|
||||||
|
"Indica durante cuántos días la nueva orden debe cubrir el stock. Si no se "
|
||||||
|
"establece, se mantiene el comportamiento por defecto del módulo."
|
||||||
|
|
||||||
|
#. module: purchase_order_product_recommendation_supermarket
|
||||||
|
#: model:ir.model.fields,help:purchase_order_product_recommendation_supermarket.field_purchase_order_recommendation__order_by_packages
|
||||||
|
msgid "Indicates if the order should be made by packages."
|
||||||
|
msgstr "Indica si el pedido debe realizarse por paquetes."
|
||||||
|
|
||||||
|
#. module: purchase_order_product_recommendation_supermarket
|
||||||
|
#: model:ir.model.fields,field_description:purchase_order_product_recommendation_supermarket.field_purchase_order_recommendation__order_by_packages
|
||||||
|
msgid "Order by packages"
|
||||||
|
msgstr "Pedir por paquetes"
|
||||||
|
|
||||||
|
#. module: purchase_order_product_recommendation_supermarket
|
||||||
|
#: model:ir.model.fields,field_description:purchase_order_product_recommendation_supermarket.field_purchase_order_recommendation__order_days
|
||||||
|
msgid "Order coverage (days)"
|
||||||
|
msgstr "Días a cubrir"
|
||||||
|
|
||||||
|
#. module: purchase_order_product_recommendation_supermarket
|
||||||
|
#: model:ir.model.fields,field_description:purchase_order_product_recommendation_supermarket.field_purchase_order_recommendation_line__packaging_id
|
||||||
|
msgid "Packaging"
|
||||||
|
msgstr "Empaquetado"
|
||||||
|
|
||||||
|
#. module: purchase_order_product_recommendation_supermarket
|
||||||
|
#: model_terms:ir.ui.view,arch_db:purchase_order_product_recommendation_supermarket.view_purchase_order_recommendation_wizard_form_supermarket
|
||||||
|
msgid "Packaging Contained Qty"
|
||||||
|
msgstr "Ctd contenida en el paquete"
|
||||||
|
|
||||||
|
#. module: purchase_order_product_recommendation_supermarket
|
||||||
|
#: model_terms:ir.ui.view,arch_db:purchase_order_product_recommendation_supermarket.view_purchase_order_recommendation_wizard_form_supermarket
|
||||||
|
msgid "Packaging Qty"
|
||||||
|
msgstr "Ctd Paquetes"
|
||||||
|
|
||||||
|
#. module: purchase_order_product_recommendation_supermarket
|
||||||
|
#: model:ir.model.fields,field_description:purchase_order_product_recommendation_supermarket.field_purchase_order_recommendation_line__packaging_qty
|
||||||
|
msgid "Packaging Quantity"
|
||||||
|
msgstr "Cantidad de paquetes"
|
||||||
|
|
||||||
|
#. module: purchase_order_product_recommendation_supermarket
|
||||||
|
#: model:ir.model.fields,field_description:purchase_order_product_recommendation_supermarket.field_purchase_order_recommendation_line__packaging_contained_qty
|
||||||
|
msgid "Packaging Quantity Contained"
|
||||||
|
msgstr "Ctd contenida en el paquete"
|
||||||
|
|
||||||
|
#. module: purchase_order_product_recommendation_supermarket
|
||||||
|
#: model_terms:ir.ui.view,arch_db:purchase_order_product_recommendation_supermarket.view_purchase_order_recommendation_wizard_form_supermarket
|
||||||
|
msgid "Qty scrapped"
|
||||||
|
msgstr "Ctd desechada"
|
||||||
|
|
||||||
|
#. module: purchase_order_product_recommendation_supermarket
|
||||||
|
#: model:ir.model.fields,help:purchase_order_product_recommendation_supermarket.field_purchase_order_recommendation_line__packaging_qty
|
||||||
|
msgid "Quantity contained in the selected packaging for this product."
|
||||||
|
msgstr "Cantidad contenida en el envase seleccionado para este producto."
|
||||||
|
|
||||||
|
#. module: purchase_order_product_recommendation_supermarket
|
||||||
|
#: model:ir.model.fields,help:purchase_order_product_recommendation_supermarket.field_purchase_order_recommendation_line__packaging_contained_qty
|
||||||
|
msgid "Quantity of packages to order for this product."
|
||||||
|
msgstr "Cantidad de paquetes a pedir para este producto."
|
||||||
|
|
||||||
|
#. module: purchase_order_product_recommendation_supermarket
|
||||||
|
#: model:ir.model,name:purchase_order_product_recommendation_supermarket.model_purchase_order_recommendation_line
|
||||||
|
msgid "Recommended product for current purchase order"
|
||||||
|
msgstr "Producto recomendado para el pedido de compra en curso"
|
||||||
|
|
||||||
|
#. module: purchase_order_product_recommendation_supermarket
|
||||||
|
#: model:ir.model,name:purchase_order_product_recommendation_supermarket.model_purchase_order_recommendation
|
||||||
|
msgid "Recommended products for current purchase order"
|
||||||
|
msgstr "Productos recomendados para el pedido de compra en curso"
|
||||||
|
|
||||||
|
#. module: purchase_order_product_recommendation_supermarket
|
||||||
|
#: model:ir.model.fields,field_description:purchase_order_product_recommendation_supermarket.field_purchase_order_recommendation__total_days
|
||||||
|
msgid "Total days"
|
||||||
|
msgstr "Días totales"
|
||||||
|
|
||||||
|
#. module: purchase_order_product_recommendation_supermarket
|
||||||
|
#: model:ir.model.fields,help:purchase_order_product_recommendation_supermarket.field_purchase_order_recommendation__total_days
|
||||||
|
msgid ""
|
||||||
|
"Total number of days between the start and end dates of the recommendation."
|
||||||
|
msgstr ""
|
||||||
|
"Número total de días entre las fechas de inicio y fin de la recomendación."
|
||||||
|
|
||||||
|
#. module: purchase_order_product_recommendation_supermarket
|
||||||
|
#: model:ir.model.fields,field_description:purchase_order_product_recommendation_supermarket.field_purchase_order_recommendation_line__units_scrapped
|
||||||
|
msgid "Units Scrapped"
|
||||||
|
msgstr "Ctd desechada"
|
||||||
|
|
@ -0,0 +1,131 @@
|
||||||
|
# Translation of Odoo Server.
|
||||||
|
# This file contains the translation of the following modules:
|
||||||
|
# * purchase_order_product_recommendation_supermarket
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: Odoo Server 16.0\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2025-06-10 15:19+0000\n"
|
||||||
|
"PO-Revision-Date: 2025-06-10 15:19+0000\n"
|
||||||
|
"Last-Translator: \n"
|
||||||
|
"Language-Team: \n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: \n"
|
||||||
|
"Plural-Forms: \n"
|
||||||
|
|
||||||
|
#. module: purchase_order_product_recommendation_supermarket
|
||||||
|
#: model:ir.model.fields,field_description:purchase_order_product_recommendation_supermarket.field_purchase_order_recommendation__avg_days_between_orders
|
||||||
|
msgid "Average days between orders"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: purchase_order_product_recommendation_supermarket
|
||||||
|
#: model:ir.model.fields,help:purchase_order_product_recommendation_supermarket.field_purchase_order_recommendation__avg_days_between_orders
|
||||||
|
msgid "Average number of days between orders for this vendor."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: purchase_order_product_recommendation_supermarket
|
||||||
|
#: model:ir.model.fields,field_description:purchase_order_product_recommendation_supermarket.field_purchase_order_recommendation_line__days_without_stock
|
||||||
|
msgid "Days without stock"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: purchase_order_product_recommendation_supermarket
|
||||||
|
#: model:ir.model.fields,help:purchase_order_product_recommendation_supermarket.field_purchase_order_recommendation__ignore_zero_stock_days
|
||||||
|
msgid ""
|
||||||
|
"If enabled, days when the product stock was 0 or less will not be considered"
|
||||||
|
" in the daily sales calculation."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: purchase_order_product_recommendation_supermarket
|
||||||
|
#: model:ir.model.fields,field_description:purchase_order_product_recommendation_supermarket.field_purchase_order_recommendation__ignore_zero_stock_days
|
||||||
|
msgid "Ignore days with zero stock"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: purchase_order_product_recommendation_supermarket
|
||||||
|
#: model:ir.model.fields,help:purchase_order_product_recommendation_supermarket.field_purchase_order_recommendation__order_days
|
||||||
|
msgid ""
|
||||||
|
"Indicate for how many days the new order should cover the stock. If not set,"
|
||||||
|
" the default module behavior is kept."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: purchase_order_product_recommendation_supermarket
|
||||||
|
#: model:ir.model.fields,help:purchase_order_product_recommendation_supermarket.field_purchase_order_recommendation__order_by_packages
|
||||||
|
msgid "Indicates if the order should be made by packages."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: purchase_order_product_recommendation_supermarket
|
||||||
|
#: model:ir.model.fields,field_description:purchase_order_product_recommendation_supermarket.field_purchase_order_recommendation__order_by_packages
|
||||||
|
msgid "Order by packages"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: purchase_order_product_recommendation_supermarket
|
||||||
|
#: model:ir.model.fields,field_description:purchase_order_product_recommendation_supermarket.field_purchase_order_recommendation__order_days
|
||||||
|
msgid "Order coverage (days)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: purchase_order_product_recommendation_supermarket
|
||||||
|
#: model:ir.model.fields,field_description:purchase_order_product_recommendation_supermarket.field_purchase_order_recommendation_line__packaging_id
|
||||||
|
msgid "Packaging"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: purchase_order_product_recommendation_supermarket
|
||||||
|
#: model_terms:ir.ui.view,arch_db:purchase_order_product_recommendation_supermarket.view_purchase_order_recommendation_wizard_form_supermarket
|
||||||
|
msgid "Packaging Contained Qty"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: purchase_order_product_recommendation_supermarket
|
||||||
|
#: model_terms:ir.ui.view,arch_db:purchase_order_product_recommendation_supermarket.view_purchase_order_recommendation_wizard_form_supermarket
|
||||||
|
msgid "Packaging Qty"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: purchase_order_product_recommendation_supermarket
|
||||||
|
#: model:ir.model.fields,field_description:purchase_order_product_recommendation_supermarket.field_purchase_order_recommendation_line__packaging_qty
|
||||||
|
msgid "Packaging Quantity"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: purchase_order_product_recommendation_supermarket
|
||||||
|
#: model:ir.model.fields,field_description:purchase_order_product_recommendation_supermarket.field_purchase_order_recommendation_line__packaging_contained_qty
|
||||||
|
msgid "Packaging Quantity Contained"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: purchase_order_product_recommendation_supermarket
|
||||||
|
#: model_terms:ir.ui.view,arch_db:purchase_order_product_recommendation_supermarket.view_purchase_order_recommendation_wizard_form_supermarket
|
||||||
|
msgid "Qty scrapped"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: purchase_order_product_recommendation_supermarket
|
||||||
|
#: model:ir.model.fields,help:purchase_order_product_recommendation_supermarket.field_purchase_order_recommendation_line__packaging_qty
|
||||||
|
msgid "Quantity contained in the selected packaging for this product."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: purchase_order_product_recommendation_supermarket
|
||||||
|
#: model:ir.model.fields,help:purchase_order_product_recommendation_supermarket.field_purchase_order_recommendation_line__packaging_contained_qty
|
||||||
|
msgid "Quantity of packages to order for this product."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: purchase_order_product_recommendation_supermarket
|
||||||
|
#: model:ir.model,name:purchase_order_product_recommendation_supermarket.model_purchase_order_recommendation_line
|
||||||
|
msgid "Recommended product for current purchase order"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: purchase_order_product_recommendation_supermarket
|
||||||
|
#: model:ir.model,name:purchase_order_product_recommendation_supermarket.model_purchase_order_recommendation
|
||||||
|
msgid "Recommended products for current purchase order"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: purchase_order_product_recommendation_supermarket
|
||||||
|
#: model:ir.model.fields,field_description:purchase_order_product_recommendation_supermarket.field_purchase_order_recommendation__total_days
|
||||||
|
msgid "Total days"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: purchase_order_product_recommendation_supermarket
|
||||||
|
#: model:ir.model.fields,help:purchase_order_product_recommendation_supermarket.field_purchase_order_recommendation__total_days
|
||||||
|
msgid ""
|
||||||
|
"Total number of days between the start and end dates of the recommendation."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#. module: purchase_order_product_recommendation_supermarket
|
||||||
|
#: model:ir.model.fields,field_description:purchase_order_product_recommendation_supermarket.field_purchase_order_recommendation_line__units_scrapped
|
||||||
|
msgid "Units Scrapped"
|
||||||
|
msgstr ""
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
from . import purchase_order_recommendation
|
||||||
|
|
@ -0,0 +1,217 @@
|
||||||
|
from odoo import api, fields, models
|
||||||
|
import math
|
||||||
|
from datetime import timedelta, datetime, time
|
||||||
|
|
||||||
|
|
||||||
|
class PurchaseOrderRecommendationSupermarketWizard(models.TransientModel):
|
||||||
|
_inherit = "purchase.order.recommendation"
|
||||||
|
|
||||||
|
order_days = fields.Integer(
|
||||||
|
string="Order coverage (days)",
|
||||||
|
help="Indicate for how many days the new order should cover the stock. If not set, the default module behavior is kept.",
|
||||||
|
)
|
||||||
|
order_by_packages = fields.Boolean(
|
||||||
|
string="Order by packages",
|
||||||
|
default=False,
|
||||||
|
help="Indicates if the order should be made by packages.",
|
||||||
|
)
|
||||||
|
ignore_zero_stock_days = fields.Boolean(
|
||||||
|
string="Ignore days with zero stock",
|
||||||
|
default=False,
|
||||||
|
help="If enabled, days when the product stock was 0 or less will not be considered in the daily sales calculation.",
|
||||||
|
)
|
||||||
|
total_days = fields.Integer(
|
||||||
|
string="Total days",
|
||||||
|
compute="_compute_total_days",
|
||||||
|
readonly=True,
|
||||||
|
help="Total number of days between the start and end dates of the recommendation.",
|
||||||
|
)
|
||||||
|
avg_days_between_orders = fields.Float(
|
||||||
|
string="Average days between orders",
|
||||||
|
compute="_compute_avg_days_between_orders",
|
||||||
|
help="Average number of days between orders for this vendor.",
|
||||||
|
readonly=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
@api.depends("date_begin", "date_end")
|
||||||
|
def _compute_total_days(self):
|
||||||
|
for rec in self:
|
||||||
|
if rec.date_begin and rec.date_end:
|
||||||
|
rec.total_days = self._get_total_days()
|
||||||
|
else:
|
||||||
|
rec.total_days = 0
|
||||||
|
|
||||||
|
@api.depends("order_id")
|
||||||
|
def _compute_avg_days_between_orders(self):
|
||||||
|
if not self.order_id.partner_id:
|
||||||
|
self.avg_days_between_orders = 0.0
|
||||||
|
return
|
||||||
|
|
||||||
|
orders = self.env["purchase.order"].search(
|
||||||
|
[
|
||||||
|
("partner_id", "=", self.order_id.partner_id.id),
|
||||||
|
("state", "in", ["purchase", "done"]),
|
||||||
|
],
|
||||||
|
order="date_order asc",
|
||||||
|
)
|
||||||
|
|
||||||
|
dates = [o.date_order.date() for o in orders if o.date_order]
|
||||||
|
if len(dates) < 2:
|
||||||
|
self.avg_days_between_orders = 0.0
|
||||||
|
return
|
||||||
|
|
||||||
|
day_diffs = [
|
||||||
|
(dates[i + 1] - dates[i]).days
|
||||||
|
for i in range(len(dates) - 1)
|
||||||
|
if (dates[i + 1] - dates[i]).days > 0
|
||||||
|
]
|
||||||
|
|
||||||
|
if not day_diffs:
|
||||||
|
self.avg_days_between_orders = 0.0
|
||||||
|
else:
|
||||||
|
self.avg_days_between_orders = sum(day_diffs) / len(day_diffs)
|
||||||
|
|
||||||
|
@api.onchange(
|
||||||
|
"order_days",
|
||||||
|
"order_by_packages",
|
||||||
|
"ignore_zero_stock_days",
|
||||||
|
)
|
||||||
|
def _onchange_order_fields(self):
|
||||||
|
self._generate_recommendations()
|
||||||
|
|
||||||
|
def _prepare_wizard_line(self, vals, order_line=False):
|
||||||
|
"""Used to create the wizard line"""
|
||||||
|
res = super()._prepare_wizard_line(vals, order_line=order_line)
|
||||||
|
product_id = order_line and order_line.product_id or vals["product_id"]
|
||||||
|
qty_to_order = res["units_included"]
|
||||||
|
|
||||||
|
if self.ignore_zero_stock_days:
|
||||||
|
days_with_stock = self._get_total_days() - self._get_days_out_of_stock(
|
||||||
|
product_id
|
||||||
|
)
|
||||||
|
res["units_avg_delivered"] = (
|
||||||
|
vals.get("qty_delivered", 0) / days_with_stock
|
||||||
|
if days_with_stock != 0
|
||||||
|
else 1
|
||||||
|
)
|
||||||
|
|
||||||
|
if self.order_days != 0:
|
||||||
|
qty_to_order = (self.order_days * res["units_avg_delivered"]) - res[
|
||||||
|
"units_virtual_available"
|
||||||
|
]
|
||||||
|
|
||||||
|
# Adjust qty_to_order to packaging multiples if order_by_packages is checked
|
||||||
|
if self.order_by_packages and product_id.packaging_ids:
|
||||||
|
packaging_qty = product_id.packaging_ids[:1].qty
|
||||||
|
if packaging_qty:
|
||||||
|
qty_to_order = math.ceil(qty_to_order / packaging_qty) * packaging_qty
|
||||||
|
res["units_included"] = qty_to_order
|
||||||
|
|
||||||
|
# Get quantities scrapped
|
||||||
|
domain = self._get_move_line_domain(product_id, src="internal", dst="inventory")
|
||||||
|
found_scrapped = self.env["stock.move.line"].read_group(
|
||||||
|
domain, ["product_id", "qty_done"], ["product_id"]
|
||||||
|
)
|
||||||
|
if len(found_scrapped):
|
||||||
|
res["units_scrapped"] = found_scrapped[0]["qty_done"]
|
||||||
|
|
||||||
|
return res
|
||||||
|
|
||||||
|
def _get_days_out_of_stock(self, product):
|
||||||
|
"""
|
||||||
|
Returns the number of days between date_begin and date_end
|
||||||
|
where the given product had zero or negative stock (qty_available).
|
||||||
|
"""
|
||||||
|
days_out_of_stock = 0
|
||||||
|
date_from = self.date_begin
|
||||||
|
date_to = self.date_end
|
||||||
|
if not date_from or not date_to:
|
||||||
|
return 0
|
||||||
|
# Loop through each day in the range
|
||||||
|
for n in range((date_to - date_from).days + 1):
|
||||||
|
day = date_from + timedelta(days=n)
|
||||||
|
qty = product.with_context(
|
||||||
|
to_date=datetime.combine(day, time(23, 59, 59))
|
||||||
|
).qty_available
|
||||||
|
if qty <= 0:
|
||||||
|
days_out_of_stock += 1
|
||||||
|
return days_out_of_stock
|
||||||
|
|
||||||
|
def _get_products(self):
|
||||||
|
"""Overwrite because filter by cateogory is not working in the base method."""
|
||||||
|
products = self._get_supplier_products()
|
||||||
|
# Filter products by category if set.
|
||||||
|
# It will apply to show_all_partner_products as well
|
||||||
|
if self.product_category_ids:
|
||||||
|
products = products.filtered(
|
||||||
|
lambda x: x.categ_id.id in self.product_category_ids.ids
|
||||||
|
)
|
||||||
|
print(self.product_category_ids)
|
||||||
|
return products
|
||||||
|
|
||||||
|
|
||||||
|
class PurchaseOrderRecommendationLine(models.TransientModel):
|
||||||
|
_inherit = "purchase.order.recommendation.line"
|
||||||
|
|
||||||
|
packaging_id = fields.Many2one(
|
||||||
|
comodel_name="product.packaging",
|
||||||
|
string="Packaging",
|
||||||
|
compute="_compute_first_packaging_id",
|
||||||
|
readonly=True,
|
||||||
|
)
|
||||||
|
packaging_qty = fields.Integer(
|
||||||
|
string="Packaging Quantity",
|
||||||
|
help="Quantity contained in the selected packaging for this product.",
|
||||||
|
compute="_compute_packaging_qty",
|
||||||
|
inverse="_inverse_packaging_qty",
|
||||||
|
store=True,
|
||||||
|
readonly=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
packaging_contained_qty = fields.Float(
|
||||||
|
string="Packaging Quantity Contained",
|
||||||
|
help="Quantity of packages to order for this product.",
|
||||||
|
related="packaging_id.qty",
|
||||||
|
readonly=True,
|
||||||
|
)
|
||||||
|
days_without_stock = fields.Integer(
|
||||||
|
string="Days without stock",
|
||||||
|
compute="_compute_days_without_stock",
|
||||||
|
readonly=True,
|
||||||
|
)
|
||||||
|
units_scrapped = fields.Float(
|
||||||
|
readonly=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
@api.depends("wizard_id.date_begin", "wizard_id.date_end")
|
||||||
|
def _compute_days_without_stock(self):
|
||||||
|
for rec in self:
|
||||||
|
if rec.product_id:
|
||||||
|
rec.days_without_stock = rec.wizard_id._get_days_out_of_stock(
|
||||||
|
rec.product_id
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
rec.days_without_stock = 0
|
||||||
|
|
||||||
|
@api.onchange("packaging_qty")
|
||||||
|
def _inverse_packaging_qty(self):
|
||||||
|
for rec in self:
|
||||||
|
if rec.packaging_id and rec.packaging_id.qty:
|
||||||
|
rec.units_included = rec.packaging_qty * rec.packaging_id.qty
|
||||||
|
|
||||||
|
@api.depends("packaging_id", "units_included")
|
||||||
|
def _compute_packaging_qty(self):
|
||||||
|
for rec in self:
|
||||||
|
if rec.packaging_id and rec.packaging_id.qty:
|
||||||
|
rec.packaging_qty = int(rec.units_included // rec.packaging_id.qty)
|
||||||
|
else:
|
||||||
|
rec.packaging_qty = 0
|
||||||
|
|
||||||
|
@api.depends("product_id")
|
||||||
|
def _compute_first_packaging_id(self):
|
||||||
|
for rec in self:
|
||||||
|
rec.packaging_id = (
|
||||||
|
rec.product_id.packaging_ids[:1].id
|
||||||
|
if rec.product_id and rec.product_id.packaging_ids
|
||||||
|
else False
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<odoo>
|
||||||
|
<record id="view_purchase_order_recommendation_wizard_form_supermarket" model="ir.ui.view">
|
||||||
|
<field name="name">purchase.order.recommendation.wizard.form.supermarket</field>
|
||||||
|
<field name="model">purchase.order.recommendation</field>
|
||||||
|
<field name="inherit_id" ref="purchase_order_product_recommendation.purchase_order_recommendation_view_form"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<field name="date_end" position="after">
|
||||||
|
<field name="total_days" />
|
||||||
|
<field name="order_days" widget="numeric_step" />
|
||||||
|
<field name="avg_days_between_orders" />
|
||||||
|
</field>
|
||||||
|
<field name="show_all_products" position="after">
|
||||||
|
<field name="order_by_packages" />
|
||||||
|
<field name="ignore_zero_stock_days" />
|
||||||
|
</field>
|
||||||
|
<field name="units_included" position="before">
|
||||||
|
<field name="units_scrapped" string="Qty scrapped" optional="hide" />
|
||||||
|
<field name="days_without_stock" optional="hide" />
|
||||||
|
<field name="packaging_id" optional="show" />
|
||||||
|
<field name="packaging_contained_qty" string="Packaging Contained Qty" optional="hide" />
|
||||||
|
<field name="packaging_qty" string="Packaging Qty" optional="hide" />
|
||||||
|
</field>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</odoo>
|
||||||
Loading…
Add table
Add a link
Reference in a new issue