From 393be1aa638fb334ecb040cfc504cfcccd465f45 Mon Sep 17 00:00:00 2001 From: robinkeunen Date: Fri, 8 Jun 2018 11:34:20 +0200 Subject: [PATCH 01/21] compute purchase order, performance fix --- compute_purchase_order/__init__.py | 1 + compute_purchase_order/__openerp__.py | 21 ++ compute_purchase_order/models/__init__.py | 4 + .../models/computed_purchase_order.py | 194 +++++++++++++++++ .../models/computed_purchase_order_line.py | 196 ++++++++++++++++++ .../models/product_template.py | 26 +++ .../models/purchase_order.py | 12 ++ .../security/ir.model.access.csv | 12 ++ .../views/computed_purchase_order.xml | 115 ++++++++++ .../views/product_template.xml | 24 +++ .../views/purchase_order.xml | 13 ++ 11 files changed, 618 insertions(+) create mode 100644 compute_purchase_order/__init__.py create mode 100644 compute_purchase_order/__openerp__.py create mode 100644 compute_purchase_order/models/__init__.py create mode 100644 compute_purchase_order/models/computed_purchase_order.py create mode 100644 compute_purchase_order/models/computed_purchase_order_line.py create mode 100644 compute_purchase_order/models/product_template.py create mode 100644 compute_purchase_order/models/purchase_order.py create mode 100644 compute_purchase_order/security/ir.model.access.csv create mode 100644 compute_purchase_order/views/computed_purchase_order.xml create mode 100644 compute_purchase_order/views/product_template.xml create mode 100644 compute_purchase_order/views/purchase_order.xml diff --git a/compute_purchase_order/__init__.py b/compute_purchase_order/__init__.py new file mode 100644 index 0000000..0650744 --- /dev/null +++ b/compute_purchase_order/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/compute_purchase_order/__openerp__.py b/compute_purchase_order/__openerp__.py new file mode 100644 index 0000000..1018692 --- /dev/null +++ b/compute_purchase_order/__openerp__.py @@ -0,0 +1,21 @@ +# -*- encoding: utf-8 -*- +{ + 'name': 'Computed Purchase Order', + 'version': '9.0.1', + 'category': 'Purchase Order', + 'description': """ todo """, + 'author': 'Coop IT Easy', + 'website': 'https://github.com/coopiteasy/procurement-addons', + 'license': 'AGPL-3', + 'depends': [ + 'product', + 'purchase', + 'stock', + 'stock_coverage', + ], + 'data': [ + 'security/ir.model.access.csv', + 'views/computed_purchase_order.xml', + 'views/purchase_order.xml', + ], +} diff --git a/compute_purchase_order/models/__init__.py b/compute_purchase_order/models/__init__.py new file mode 100644 index 0000000..6fdf87d --- /dev/null +++ b/compute_purchase_order/models/__init__.py @@ -0,0 +1,4 @@ +from . import purchase_order +from . import computed_purchase_order +from . import computed_purchase_order_line +from . import product_template diff --git a/compute_purchase_order/models/computed_purchase_order.py b/compute_purchase_order/models/computed_purchase_order.py new file mode 100644 index 0000000..2c4bb62 --- /dev/null +++ b/compute_purchase_order/models/computed_purchase_order.py @@ -0,0 +1,194 @@ +# -*- coding: utf-8 -*- +from openerp import models, fields, api +from openerp.exceptions import ValidationError + + +class ComputedPurchaseOrder(models.Model): + _description = 'Computed Purchase Order' + _name = 'computed.purchase.order' + _order = 'id desc' + + name = fields.Char( + string='CPO Reference', + size=64, + default='New') + + order_date = fields.Datetime( + string='Purchase Order Date', + default=fields.Datetime.now, + help="Depicts the date where the Quotation should be validated and converted into a purchase order.") # noqa + + date_planned = fields.Datetime( + string='Date Planned' + ) + + supplier_id = fields.Many2one( + 'res.partner', + string='Supplier', + readonly=True, + help="Supplier of the purchase order.") + + order_line_ids = fields.One2many( + 'computed.purchase.order.line', + 'computed_purchase_order_id', + string='Order Lines', + ) + + total_amount = fields.Float( + string='Total Amount (w/o VAT)', + compute='_compute_cpo_total' + ) + + generated_purchase_order_ids = fields.One2many( + 'purchase.order', + 'original_cpo_id', + string='Generated Purchase Orders', + ) + + generated_po_count = fields.Integer( + string='Generated Purchase Order count', + compute='_compute_generated_po_count', + ) + + @api.model + def default_get(self, fields_list): + record = super(ComputedPurchaseOrder, self).default_get(fields_list) + + record['date_planned'] = self._get_default_date_planned() + record['supplier_id'] = self._get_selected_supplier_id() + record['order_line_ids'] = self._create_order_lines() + record['name'] = self._compute_default_name() + + return record + + def _get_default_date_planned(self): + return fields.Datetime.now() + + def _get_selected_supplier_id(self): + """ + Calcule le vendeur associé qui a la date de début la plus récente et + plus petite qu’aujourd’hui pour chaque article sélectionné. + Will raise an error if more than two sellers are set + """ + if 'active_ids' not in self.env.context: + return False + + product_ids = self.env.context['active_ids'] + products = self.env['product.template'].browse(product_ids) + + suppliers = set() + for product in products: + main_supplier_id = product.main_supplier_id.id + suppliers.add(main_supplier_id) + + if len(suppliers) == 0: + raise ValidationError(u'No supplier is set for selected articles.') + elif len(suppliers) == 1: + return suppliers.pop() + else: + raise ValidationError( + u'You must select article from a single supplier.') + + def _create_order_lines(self): + product_tmpl_ids = self._get_selected_products() + cpol_ids = [] + OrderLine = self.env['computed.purchase.order.line'] + for product_id in product_tmpl_ids: + cpol = OrderLine.create( + {'computed_purchase_order_id': self.id, + 'product_template_id': product_id, + } + ) + # should ideally be set in cpol defaults + cpol.purchase_quantity = cpol.minimum_purchase_qty + cpol_ids.append(cpol.id) + return cpol_ids + + def _compute_default_name(self): + supplier_id = self._get_selected_supplier_id() + if supplier_id: + supplier_name = ( + self.env['res.partner'] + .browse(supplier_id) + .name) + + name = u'CPO {} {}'.format( + supplier_name, + fields.Date.today()) + else: + name = 'New' + return name + + def _get_selected_products(self): + if 'active_ids' in self.env.context: + return self.env.context['active_ids'] + else: + return [] + + @api.multi + def _compute_generated_po_count(self): + for cpo in self: + cpo.generated_po_count = len(cpo.generated_purchase_order_ids) + + @api.multi + def get_generated_po_action(self): + self.ensure_one() + action = { + 'type': 'ir.actions.act_window', + 'res_model': 'purchase.order', + 'view_mode': 'tree,form,kanban', + 'target': 'current', + 'domain': [('id', 'in', self.generated_purchase_order_ids.ids)], + } + return action + + # @api.onchange(order_line_ids) # fixme + @api.multi + def _compute_cpo_total(self): + for cpo in self: + total_amount = sum(cpol.subtotal for cpol in cpo.order_line_ids) + cpo.total_amount = total_amount + + @api.multi + def create_purchase_order(self): + self.ensure_one() + + if sum(self.order_line_ids.mapped('purchase_quantity')) == 0: + raise ValidationError(u'You need at least a product to generate ' + u'a Purchase Order') + + PurchaseOrder = self.env['purchase.order'] + PurchaseOrderLine = self.env['purchase.order.line'] + + po_values = { + 'name': 'New', + 'date_order': self.order_date, + 'partner_id': self.supplier_id.id, + 'date_planned': self.date_planned, + } + purchase_order = PurchaseOrder.create(po_values) + + for cpo_line in self.order_line_ids: + if cpo_line.purchase_quantity > 0: + pol_values = { + 'name': cpo_line.name, + 'product_id': cpo_line.get_default_product_product().id, + 'product_qty': cpo_line.purchase_quantity, + 'price_unit': cpo_line.product_price, + 'product_uom': cpo_line.uom_po_id.id, + 'order_id': purchase_order.id, + 'date_planned': self.date_planned, + } + PurchaseOrderLine.create(pol_values) + + self.generated_purchase_order_ids += purchase_order + + action = { + 'type': 'ir.actions.act_window', + 'res_model': 'purchase.order', + 'res_id': purchase_order.id, + 'view_type': 'form', + 'view_mode': 'form', + 'target': 'current', + } + return action diff --git a/compute_purchase_order/models/computed_purchase_order_line.py b/compute_purchase_order/models/computed_purchase_order_line.py new file mode 100644 index 0000000..92ca505 --- /dev/null +++ b/compute_purchase_order/models/computed_purchase_order_line.py @@ -0,0 +1,196 @@ +# -*- coding: utf-8 -*- +import logging + +from openerp import models, fields, api +from openerp.exceptions import ValidationError + +_logger = logging.getLogger(__name__) + + +class ComputedPurchaseOrderLine(models.Model): + _description = 'Computed Purchase Order Line' + _name = 'computed.purchase.order.line' + + computed_purchase_order_id = fields.Many2one( + 'computed.purchase.order', + string='Computed Purchase Order', + ) + + product_template_id = fields.Many2one( + 'product.template', + string='Linked Product Template', + required=True, + help='Product') + + name = fields.Char( + string='Product Name', + related='product_template_id.name', + read_only=True) + + supplierinfo_id = fields.Many2one( + 'product.supplierinfo', + string='Supplier information', + compute='_compute_supplierinfo', + store=True, + readonly=True, + ) + + category_id = fields.Many2one( + 'product.category', + string='Internal Category', + related='product_template_id.categ_id', + read_only=True) + + uom_id = fields.Many2one( + 'product.uom', + string='Unit of Measure', + read_only=True, + related='product_template_id.uom_id', + help="Default Unit of Measure used for all stock operation.") + + qty_available = fields.Float( + string='Stock Quantity', + related='product_template_id.qty_available', + read_only=True, + help='Quantity currently in stock. Does not take ' + 'into account incoming orders.') + + virtual_available = fields.Float( + string='Stock Quantity', + related='product_template_id.virtual_available', + read_only=True, + help='Virtual quantity taking into account current stock, incoming ' + 'orders and outgoing sales.') + + average_consumption = fields.Float( + string='Average Consumption', + related='product_template_id.average_consumption', + read_only=True) + + stock_coverage = fields.Float( + string='Stock Coverage', + related='product_template_id.estimated_stock_coverage', + read_only=True, + ) + + minimum_purchase_qty = fields.Float( + string='Minimum Purchase Quantity', + compute='_depends_on_product_template', + ) + + purchase_quantity = fields.Float( + string='Purchase Quantity', + required=True, + default=0.) + + uom_po_id = fields.Many2one( + 'product.uom', + string='Purchase Unit of Measure', + read_only=True, + related='product_template_id.uom_po_id', + help="Default Unit of Measure used for all stock operation.") + + product_price = fields.Float( + string='Product Price (w/o VAT)', + compute='_depends_on_product_template', + read_only=True, + help='Supplier Product Price by buying unit. Price is without VAT') + + virtual_coverage = fields.Float( + string='Expected Stock Coverage', + compute='_depends_on_purchase_quantity', + help='Expected stock coverage (in days) based on current stocks and average daily consumption') # noqa + + subtotal = fields.Float( + string='Subtotal (w/o VAT)', + compute='_depends_on_purchase_quantity') + + @api.multi + @api.depends('product_template_id') + def _depends_on_product_template(self): + for cpol in self: + # get supplier info + cpol.minimum_purchase_qty = cpol.supplierinfo_id.min_qty + cpol.product_price = cpol.supplierinfo_id.price + + @api.multi + @api.onchange('product_template_id') + def _onchange_purchase_quantity(self): + for cpol in self: + cpol.purchase_quantity = cpol.supplierinfo_id.min_qty + + @api.depends('purchase_quantity') + @api.multi + def _depends_on_purchase_quantity(self): + for cpol in self: + cpol.subtotal = cpol.product_price * cpol.purchase_quantity + avg = cpol.average_consumption + if avg > 0: + qty = cpol.virtual_available + cpol.purchase_quantity + cpol.virtual_coverage = qty / avg + else: + # todo what would be a good default value? (not float(inf)) + cpol.virtual_coverage = 9999 + + return True + + @api.multi + @api.depends('product_template_id') + @api.onchange('product_template_id') + def _compute_supplierinfo(self): + for cpol in self: + if not cpol.product_template_id: + cpol.supplierinfo_id = False + else: + SupplierInfo = self.env['product.supplierinfo'] + si = SupplierInfo.search([ + ('product_tmpl_id', '=', cpol.product_template_id.id), + ('name', '=', cpol.product_template_id.main_supplier_id.id) # noqa + ]) + + if len(si) == 0: + raise ValidationError( + u'No supplier information set for {name}' + .format(name=cpol.product_template_id.name)) + elif len(si) == 1: + cpol.supplierinfo_id = si + else: + _logger.warning( + u'product {name} has several suppliers, chose last' + .format(name=cpol.product_template_id.name) + ) + si = si.sorted(key=lambda r: r.create_date, reverse=True) + cpol.supplierinfo_id = si[0] + + @api.constrains('purchase_quantity') + def _check_minimum_purchase_quantity(self): + for cpol in self: + if cpol.purchase_quantity < 0: + raise ValidationError(u'Purchase quantity for {product_name} must be greater than 0' # noqa + .format(product_name=cpol.product_template_id.name)) + elif 0 < cpol.purchase_quantity < cpol.minimum_purchase_qty: + raise ValidationError(u'Purchase quantity for {product_name} must be greater than {min_qty}' # noqa + .format(product_name=cpol.product_template_id.name, + min_qty=cpol.minimum_purchase_qty)) + + @api.multi + def get_default_product_product(self): + self.ensure_one() + ProductProduct = self.env['product.product'] + products = ProductProduct.search([ + ('product_tmpl_id', '=', self.product_template_id.id) + ]) + + products = products.sorted( + key=lambda product: product.create_date, + reverse=True + ) + + if products: + return products[0] + else: + raise ValidationError( + u'%s:%s template has no variant set' + % (self.product_template_id.id, self.product_template_id.name) + ) + diff --git a/compute_purchase_order/models/product_template.py b/compute_purchase_order/models/product_template.py new file mode 100644 index 0000000..030719b --- /dev/null +++ b/compute_purchase_order/models/product_template.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +from openerp import models, fields, api + + +class ProductTemplate(models.Model): + _inherit = "product.template" + + main_supplier_id = fields.Many2one( + 'res.partner', + compute='_compute_main_supplier_id', + store=True + ) + + def _get_sorted_supplierinfo(self): + return self.seller_ids.sorted( + key=lambda seller: seller.date_start, + reverse=True) + + @api.multi + @api.depends('seller_ids', 'seller_ids.date_start') + def _compute_main_supplier_id(self): + # Calcule le vendeur associé qui a la date de début la plus récente + # et plus petite qu’aujourd’hui + for pt in self: + sellers_ids = pt._get_sorted_supplierinfo() + pt.main_supplier_id = sellers_ids and sellers_ids[0].name or False diff --git a/compute_purchase_order/models/purchase_order.py b/compute_purchase_order/models/purchase_order.py new file mode 100644 index 0000000..24da3c6 --- /dev/null +++ b/compute_purchase_order/models/purchase_order.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- +from openerp import models, fields + + +class PurchaseOrder(models.Model): + _inherit = "purchase.order" + + original_cpo_id = fields.Many2one( + 'computed.purchase.order', + string='Original CPO', + help='CPO used to generate this Purchase Order' + ) diff --git a/compute_purchase_order/security/ir.model.access.csv b/compute_purchase_order/security/ir.model.access.csv new file mode 100644 index 0000000..faf22c8 --- /dev/null +++ b/compute_purchase_order/security/ir.model.access.csv @@ -0,0 +1,12 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink + +access_purchase_order,purchase.order,model_computed_purchase_order,purchase.group_purchase_user,1,1,1,1 +access_purchase_order_manager,purchase.order,model_computed_purchase_order,purchase.group_purchase_manager,1,1,1,1 +access_purchase_order_stock_worker,purchase.order,model_computed_purchase_order,stock.group_stock_user,1,0,0,0 +access_purchase_order_invoicing_payments,purchase.order,model_computed_purchase_order,account.group_account_invoice,1,1,0,0 + +access_purchase_order_line,purchase.order.line user,model_computed_purchase_order_line,purchase.group_purchase_user,1,1,1,1 +access_purchase_order_line_manager,purchase.order.line manager,model_computed_purchase_order_line,purchase.group_purchase_manager,1,0,0,0 +access_purchase_order_line_stock_worker,purchase.order.line,model_computed_purchase_order_line,stock.group_stock_user,1,0,0,0 +access_purchase_order_line_manager,purchase.order.line,model_computed_purchase_order_line,purchase.group_purchase_manager,1,1,1,1 +access_purchase_order_line_invoicing_payments,purchase.order.line,model_computed_purchase_order_line,account.group_account_invoice,1,1,0,0 diff --git a/compute_purchase_order/views/computed_purchase_order.xml b/compute_purchase_order/views/computed_purchase_order.xml new file mode 100644 index 0000000..10038e1 --- /dev/null +++ b/compute_purchase_order/views/computed_purchase_order.xml @@ -0,0 +1,115 @@ + + + + + + + computed.purchase.order.tree + computed.purchase.order + + + + + + + + + + + + + + computed.purchase.order.form + computed.purchase.order + +
+
+
+ + + + + + + + + + + +
+ +
+ +
+ + + + + + + + + + + + + + + + +
+ + +
+
+
+ + + + computed.purchase.order + + + + + + + + + + + Computed Purchase Orders + ir.actions.act_window + computed.purchase.order + tree,form + + + + + + + + + +
diff --git a/compute_purchase_order/views/product_template.xml b/compute_purchase_order/views/product_template.xml new file mode 100644 index 0000000..c5607c8 --- /dev/null +++ b/compute_purchase_order/views/product_template.xml @@ -0,0 +1,24 @@ + + + + + product.template.tree + product.template + + + + + + + + + + + + + + + + + + diff --git a/compute_purchase_order/views/purchase_order.xml b/compute_purchase_order/views/purchase_order.xml new file mode 100644 index 0000000..75dc354 --- /dev/null +++ b/compute_purchase_order/views/purchase_order.xml @@ -0,0 +1,13 @@ + + + + purchase.order.form.inherit + purchase.order + + + + + + + + From b74fa7b4db13372f4cb7b54fea39b5cc039fb9a1 Mon Sep 17 00:00:00 2001 From: robinkeunen Date: Fri, 15 Jun 2018 18:45:40 +0200 Subject: [PATCH 02/21] [IMP] add product code in product name on PO creaption from CPO --- compute_purchase_order/models/computed_purchase_order.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/compute_purchase_order/models/computed_purchase_order.py b/compute_purchase_order/models/computed_purchase_order.py index 2c4bb62..112ead8 100644 --- a/compute_purchase_order/models/computed_purchase_order.py +++ b/compute_purchase_order/models/computed_purchase_order.py @@ -170,8 +170,12 @@ class ComputedPurchaseOrder(models.Model): for cpo_line in self.order_line_ids: if cpo_line.purchase_quantity > 0: + if cpo_line.supplierinfo_id.product_code: + pol_name = '[%s] %s' % (cpo_line.supplierinfo_id.product_code, cpo_line.name) + else: + pol_name = cpo_line.name pol_values = { - 'name': cpo_line.name, + 'name': pol_name, 'product_id': cpo_line.get_default_product_product().id, 'product_qty': cpo_line.purchase_quantity, 'price_unit': cpo_line.product_price, From b90adcafcc46babe6a8795e007da0f82dc005cda Mon Sep 17 00:00:00 2001 From: robinkeunen Date: Fri, 15 Jun 2018 18:51:03 +0200 Subject: [PATCH 03/21] remove category colonne (useless here) --- compute_purchase_order/views/computed_purchase_order.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/compute_purchase_order/views/computed_purchase_order.xml b/compute_purchase_order/views/computed_purchase_order.xml index 10038e1..16c94f1 100644 --- a/compute_purchase_order/views/computed_purchase_order.xml +++ b/compute_purchase_order/views/computed_purchase_order.xml @@ -57,7 +57,6 @@ - From 70b1fe36764b896f266852e22b42b13d65ab66e2 Mon Sep 17 00:00:00 2001 From: robinkeunen Date: Thu, 2 Aug 2018 16:47:12 +0200 Subject: [PATCH 04/21] compute purchase order line taxes id --- .../models/computed_purchase_order.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/compute_purchase_order/models/computed_purchase_order.py b/compute_purchase_order/models/computed_purchase_order.py index 112ead8..00a2e56 100644 --- a/compute_purchase_order/models/computed_purchase_order.py +++ b/compute_purchase_order/models/computed_purchase_order.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from openerp import models, fields, api +from openerp import models, fields, api, SUPERUSER_ID from openerp.exceptions import ValidationError @@ -183,7 +183,8 @@ class ComputedPurchaseOrder(models.Model): 'order_id': purchase_order.id, 'date_planned': self.date_planned, } - PurchaseOrderLine.create(pol_values) + pol = PurchaseOrderLine.create(pol_values) + pol.compute_taxes_id() self.generated_purchase_order_ids += purchase_order @@ -196,3 +197,17 @@ class ComputedPurchaseOrder(models.Model): 'target': 'current', } return action + + +class PurchaseOrderLine(models.Model): + _inherit = 'purchase.order.line' + + @api.multi + def compute_taxes_id(self): + for pol in self: + fpos = pol.order_id.fiscal_position_id + if self.env.uid == SUPERUSER_ID: + company_id = self.env.user.company_id.id + pol.taxes_id = fpos.map_tax(pol.product_id.supplier_taxes_id.filtered(lambda r: r.company_id.id == company_id)) + else: + pol.taxes_id = fpos.map_tax(pol.product_id.supplier_taxes_id) From 2634067c7bb41b6d53003244d3f9b8b5dc6de36a Mon Sep 17 00:00:00 2001 From: "robin.keunen" Date: Wed, 3 Apr 2019 16:26:06 +0200 Subject: [PATCH 05/21] [FIX] cpo: use uom_po_id.factor to compute expected coverage --- compute_purchase_order/models/computed_purchase_order_line.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compute_purchase_order/models/computed_purchase_order_line.py b/compute_purchase_order/models/computed_purchase_order_line.py index 92ca505..3e82b65 100644 --- a/compute_purchase_order/models/computed_purchase_order_line.py +++ b/compute_purchase_order/models/computed_purchase_order_line.py @@ -126,7 +126,8 @@ class ComputedPurchaseOrderLine(models.Model): cpol.subtotal = cpol.product_price * cpol.purchase_quantity avg = cpol.average_consumption if avg > 0: - qty = cpol.virtual_available + cpol.purchase_quantity + qty = ((cpol.virtual_available / cpol.uom_id.factor) + + (cpol.purchase_quantity / cpol.uom_po_id.factor)) cpol.virtual_coverage = qty / avg else: # todo what would be a good default value? (not float(inf)) @@ -193,4 +194,3 @@ class ComputedPurchaseOrderLine(models.Model): u'%s:%s template has no variant set' % (self.product_template_id.id, self.product_template_id.name) ) - From 10a465c8951d3ebd229862551d76d9093e91e4d9 Mon Sep 17 00:00:00 2001 From: "robin.keunen" Date: Wed, 3 Apr 2019 17:05:11 +0200 Subject: [PATCH 06/21] [FIX] cpo: compute fiscal position --- .../models/computed_purchase_order.py | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/compute_purchase_order/models/computed_purchase_order.py b/compute_purchase_order/models/computed_purchase_order.py index 00a2e56..87973b8 100644 --- a/compute_purchase_order/models/computed_purchase_order.py +++ b/compute_purchase_order/models/computed_purchase_order.py @@ -205,9 +205,24 @@ class PurchaseOrderLine(models.Model): @api.multi def compute_taxes_id(self): for pol in self: - fpos = pol.order_id.fiscal_position_id if self.env.uid == SUPERUSER_ID: company_id = self.env.user.company_id.id - pol.taxes_id = fpos.map_tax(pol.product_id.supplier_taxes_id.filtered(lambda r: r.company_id.id == company_id)) else: - pol.taxes_id = fpos.map_tax(pol.product_id.supplier_taxes_id) + company_id = self.company_id.id + + fpos_id = ( + self.env['account.fiscal.position'] + .with_context(company_id=company_id) + .get_fiscal_position(pol.partner_id.id) + ) + fpos = self.env['account.fiscal.position'].browse(fpos_id) + pol.order_id.fiscal_position_id = fpos + + taxes = self.product_id.supplier_taxes_id + taxes_id = fpos.map_tax(taxes) if fpos else taxes + + if taxes_id: + taxes_id = taxes_id.filtered( + lambda t: t.company_id.id == company_id) + + pol.taxes_id = taxes_id From 6154a98329c467128541b992bcc41a8b1ca26867 Mon Sep 17 00:00:00 2001 From: "robin.keunen" Date: Fri, 10 May 2019 14:00:46 +0200 Subject: [PATCH 07/21] [IMP] cpo: add forecast qty to cpo view --- compute_purchase_order/models/computed_purchase_order_line.py | 2 +- compute_purchase_order/views/computed_purchase_order.xml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/compute_purchase_order/models/computed_purchase_order_line.py b/compute_purchase_order/models/computed_purchase_order_line.py index 3e82b65..1b9a4f8 100644 --- a/compute_purchase_order/models/computed_purchase_order_line.py +++ b/compute_purchase_order/models/computed_purchase_order_line.py @@ -56,7 +56,7 @@ class ComputedPurchaseOrderLine(models.Model): 'into account incoming orders.') virtual_available = fields.Float( - string='Stock Quantity', + string='Forecast Quantity', related='product_template_id.virtual_available', read_only=True, help='Virtual quantity taking into account current stock, incoming ' diff --git a/compute_purchase_order/views/computed_purchase_order.xml b/compute_purchase_order/views/computed_purchase_order.xml index 16c94f1..c84432b 100644 --- a/compute_purchase_order/views/computed_purchase_order.xml +++ b/compute_purchase_order/views/computed_purchase_order.xml @@ -58,6 +58,7 @@ + From 8776fb2242a68d87c892df8ae565ac3b4412dc37 Mon Sep 17 00:00:00 2001 From: "robin.keunen" Date: Fri, 10 May 2019 14:08:31 +0200 Subject: [PATCH 08/21] [IMP] cpo: store total_amount (performance) --- compute_purchase_order/models/computed_purchase_order.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/compute_purchase_order/models/computed_purchase_order.py b/compute_purchase_order/models/computed_purchase_order.py index 87973b8..6ce507d 100644 --- a/compute_purchase_order/models/computed_purchase_order.py +++ b/compute_purchase_order/models/computed_purchase_order.py @@ -36,7 +36,8 @@ class ComputedPurchaseOrder(models.Model): total_amount = fields.Float( string='Total Amount (w/o VAT)', - compute='_compute_cpo_total' + compute='_compute_cpo_total', + store=True, ) generated_purchase_order_ids = fields.One2many( @@ -142,7 +143,7 @@ class ComputedPurchaseOrder(models.Model): } return action - # @api.onchange(order_line_ids) # fixme + @api.depends('order_line_ids.subtotal') @api.multi def _compute_cpo_total(self): for cpo in self: From af1ffcb465474da931531fee2246f3186254d690 Mon Sep 17 00:00:00 2001 From: Vincent Van Rossem Date: Thu, 16 Apr 2020 14:47:49 +0200 Subject: [PATCH 09/21] [MIG] compute_purchase_order: migration to 10.0 - followed oca guidelines - not tested on odoo 10.0 --- compute_purchase_order/{__openerp__.py => __manifest__.py} | 2 +- compute_purchase_order/models/computed_purchase_order.py | 4 ++-- compute_purchase_order/models/computed_purchase_order_line.py | 4 ++-- compute_purchase_order/models/product_template.py | 2 +- compute_purchase_order/models/purchase_order.py | 2 +- compute_purchase_order/views/computed_purchase_order.xml | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) rename compute_purchase_order/{__openerp__.py => __manifest__.py} (94%) diff --git a/compute_purchase_order/__openerp__.py b/compute_purchase_order/__manifest__.py similarity index 94% rename from compute_purchase_order/__openerp__.py rename to compute_purchase_order/__manifest__.py index 1018692..5b0d038 100644 --- a/compute_purchase_order/__openerp__.py +++ b/compute_purchase_order/__manifest__.py @@ -1,7 +1,7 @@ # -*- encoding: utf-8 -*- { 'name': 'Computed Purchase Order', - 'version': '9.0.1', + 'version': '10.0.1.0.0', 'category': 'Purchase Order', 'description': """ todo """, 'author': 'Coop IT Easy', diff --git a/compute_purchase_order/models/computed_purchase_order.py b/compute_purchase_order/models/computed_purchase_order.py index 6ce507d..5cf0769 100644 --- a/compute_purchase_order/models/computed_purchase_order.py +++ b/compute_purchase_order/models/computed_purchase_order.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -from openerp import models, fields, api, SUPERUSER_ID -from openerp.exceptions import ValidationError +from odoo import models, fields, api, SUPERUSER_ID +from odoo.exceptions import ValidationError class ComputedPurchaseOrder(models.Model): diff --git a/compute_purchase_order/models/computed_purchase_order_line.py b/compute_purchase_order/models/computed_purchase_order_line.py index 1b9a4f8..161d48e 100644 --- a/compute_purchase_order/models/computed_purchase_order_line.py +++ b/compute_purchase_order/models/computed_purchase_order_line.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- import logging -from openerp import models, fields, api -from openerp.exceptions import ValidationError +from odoo import models, fields, api +from odoo.exceptions import ValidationError _logger = logging.getLogger(__name__) diff --git a/compute_purchase_order/models/product_template.py b/compute_purchase_order/models/product_template.py index 030719b..83947be 100644 --- a/compute_purchase_order/models/product_template.py +++ b/compute_purchase_order/models/product_template.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from openerp import models, fields, api +from odoo import models, fields, api class ProductTemplate(models.Model): diff --git a/compute_purchase_order/models/purchase_order.py b/compute_purchase_order/models/purchase_order.py index 24da3c6..e82032d 100644 --- a/compute_purchase_order/models/purchase_order.py +++ b/compute_purchase_order/models/purchase_order.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from openerp import models, fields +from odoo import models, fields class PurchaseOrder(models.Model): diff --git a/compute_purchase_order/views/computed_purchase_order.xml b/compute_purchase_order/views/computed_purchase_order.xml index c84432b..fe9efdd 100644 --- a/compute_purchase_order/views/computed_purchase_order.xml +++ b/compute_purchase_order/views/computed_purchase_order.xml @@ -55,7 +55,7 @@ - + From 713235e0e308bf76a6530d53b2e88f6c97f3a090 Mon Sep 17 00:00:00 2001 From: Vincent Van Rossem Date: Thu, 16 Apr 2020 14:50:10 +0200 Subject: [PATCH 10/21] [MIG] compute_purchase_order: migration to 11.0 - followed oca guidelines - not tested on odoo 11.0 --- compute_purchase_order/__manifest__.py | 3 +-- .../models/computed_purchase_order.py | 11 +++++------ .../models/computed_purchase_order_line.py | 11 +++++------ compute_purchase_order/models/product_template.py | 1 - compute_purchase_order/models/purchase_order.py | 1 - 5 files changed, 11 insertions(+), 16 deletions(-) diff --git a/compute_purchase_order/__manifest__.py b/compute_purchase_order/__manifest__.py index 5b0d038..69d8b12 100644 --- a/compute_purchase_order/__manifest__.py +++ b/compute_purchase_order/__manifest__.py @@ -1,7 +1,6 @@ -# -*- encoding: utf-8 -*- { 'name': 'Computed Purchase Order', - 'version': '10.0.1.0.0', + 'version': '11.0.1.0.0', 'category': 'Purchase Order', 'description': """ todo """, 'author': 'Coop IT Easy', diff --git a/compute_purchase_order/models/computed_purchase_order.py b/compute_purchase_order/models/computed_purchase_order.py index 5cf0769..9df5623 100644 --- a/compute_purchase_order/models/computed_purchase_order.py +++ b/compute_purchase_order/models/computed_purchase_order.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- from odoo import models, fields, api, SUPERUSER_ID from odoo.exceptions import ValidationError @@ -83,12 +82,12 @@ class ComputedPurchaseOrder(models.Model): suppliers.add(main_supplier_id) if len(suppliers) == 0: - raise ValidationError(u'No supplier is set for selected articles.') + raise ValidationError('No supplier is set for selected articles.') elif len(suppliers) == 1: return suppliers.pop() else: raise ValidationError( - u'You must select article from a single supplier.') + 'You must select article from a single supplier.') def _create_order_lines(self): product_tmpl_ids = self._get_selected_products() @@ -113,7 +112,7 @@ class ComputedPurchaseOrder(models.Model): .browse(supplier_id) .name) - name = u'CPO {} {}'.format( + name = 'CPO {} {}'.format( supplier_name, fields.Date.today()) else: @@ -155,8 +154,8 @@ class ComputedPurchaseOrder(models.Model): self.ensure_one() if sum(self.order_line_ids.mapped('purchase_quantity')) == 0: - raise ValidationError(u'You need at least a product to generate ' - u'a Purchase Order') + raise ValidationError('You need at least a product to generate ' + 'a Purchase Order') PurchaseOrder = self.env['purchase.order'] PurchaseOrderLine = self.env['purchase.order.line'] diff --git a/compute_purchase_order/models/computed_purchase_order_line.py b/compute_purchase_order/models/computed_purchase_order_line.py index 161d48e..9963c2e 100644 --- a/compute_purchase_order/models/computed_purchase_order_line.py +++ b/compute_purchase_order/models/computed_purchase_order_line.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import logging from odoo import models, fields, api @@ -151,13 +150,13 @@ class ComputedPurchaseOrderLine(models.Model): if len(si) == 0: raise ValidationError( - u'No supplier information set for {name}' + 'No supplier information set for {name}' .format(name=cpol.product_template_id.name)) elif len(si) == 1: cpol.supplierinfo_id = si else: _logger.warning( - u'product {name} has several suppliers, chose last' + 'product {name} has several suppliers, chose last' .format(name=cpol.product_template_id.name) ) si = si.sorted(key=lambda r: r.create_date, reverse=True) @@ -167,10 +166,10 @@ class ComputedPurchaseOrderLine(models.Model): def _check_minimum_purchase_quantity(self): for cpol in self: if cpol.purchase_quantity < 0: - raise ValidationError(u'Purchase quantity for {product_name} must be greater than 0' # noqa + raise ValidationError('Purchase quantity for {product_name} must be greater than 0' # noqa .format(product_name=cpol.product_template_id.name)) elif 0 < cpol.purchase_quantity < cpol.minimum_purchase_qty: - raise ValidationError(u'Purchase quantity for {product_name} must be greater than {min_qty}' # noqa + raise ValidationError('Purchase quantity for {product_name} must be greater than {min_qty}' # noqa .format(product_name=cpol.product_template_id.name, min_qty=cpol.minimum_purchase_qty)) @@ -191,6 +190,6 @@ class ComputedPurchaseOrderLine(models.Model): return products[0] else: raise ValidationError( - u'%s:%s template has no variant set' + '%s:%s template has no variant set' % (self.product_template_id.id, self.product_template_id.name) ) diff --git a/compute_purchase_order/models/product_template.py b/compute_purchase_order/models/product_template.py index 83947be..61bf5e9 100644 --- a/compute_purchase_order/models/product_template.py +++ b/compute_purchase_order/models/product_template.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- from odoo import models, fields, api diff --git a/compute_purchase_order/models/purchase_order.py b/compute_purchase_order/models/purchase_order.py index e82032d..2c57db6 100644 --- a/compute_purchase_order/models/purchase_order.py +++ b/compute_purchase_order/models/purchase_order.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- from odoo import models, fields From 0f802d97e65bdb4159d87531b89c89fcdeb7b69e Mon Sep 17 00:00:00 2001 From: Vincent Van Rossem Date: Thu, 16 Apr 2020 14:54:45 +0200 Subject: [PATCH 11/21] [MIG] compute_purchase_order: migration to 12.0 - followed oca guidelines --- compute_purchase_order/__manifest__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compute_purchase_order/__manifest__.py b/compute_purchase_order/__manifest__.py index 69d8b12..2c0f38a 100644 --- a/compute_purchase_order/__manifest__.py +++ b/compute_purchase_order/__manifest__.py @@ -1,6 +1,6 @@ { 'name': 'Computed Purchase Order', - 'version': '11.0.1.0.0', + 'version': '12.0.1.0.0', 'category': 'Purchase Order', 'description': """ todo """, 'author': 'Coop IT Easy', From b1bdf152906ce7ab13e4942d5d71873627c77fd0 Mon Sep 17 00:00:00 2001 From: Vincent Van Rossem Date: Thu, 16 Apr 2020 15:16:55 +0200 Subject: [PATCH 12/21] [FIX] compute_purchase_order: stock_coverage has been renamed --- compute_purchase_order/__manifest__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compute_purchase_order/__manifest__.py b/compute_purchase_order/__manifest__.py index 2c0f38a..d7f1427 100644 --- a/compute_purchase_order/__manifest__.py +++ b/compute_purchase_order/__manifest__.py @@ -10,7 +10,7 @@ 'product', 'purchase', 'stock', - 'stock_coverage', + 'beesdoo_stock_coverage', ], 'data': [ 'security/ir.model.access.csv', From e51e22f7566bd30fc6fd0f321525fcf1c8ee520b Mon Sep 17 00:00:00 2001 From: Vincent Van Rossem Date: Thu, 16 Apr 2020 15:32:31 +0200 Subject: [PATCH 13/21] [FIX] compute_purchase_order: fields renamed in beesdoo_stock_coverage --- .../models/computed_purchase_order_line.py | 8 ++++---- compute_purchase_order/views/computed_purchase_order.xml | 2 +- compute_purchase_order/views/product_template.xml | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/compute_purchase_order/models/computed_purchase_order_line.py b/compute_purchase_order/models/computed_purchase_order_line.py index 9963c2e..a0f3882 100644 --- a/compute_purchase_order/models/computed_purchase_order_line.py +++ b/compute_purchase_order/models/computed_purchase_order_line.py @@ -61,14 +61,14 @@ class ComputedPurchaseOrderLine(models.Model): help='Virtual quantity taking into account current stock, incoming ' 'orders and outgoing sales.') - average_consumption = fields.Float( + daily_sales = fields.Float( string='Average Consumption', - related='product_template_id.average_consumption', + related='product_template_id.daily_sales', read_only=True) stock_coverage = fields.Float( string='Stock Coverage', - related='product_template_id.estimated_stock_coverage', + related='product_template_id.stock_coverage', read_only=True, ) @@ -123,7 +123,7 @@ class ComputedPurchaseOrderLine(models.Model): def _depends_on_purchase_quantity(self): for cpol in self: cpol.subtotal = cpol.product_price * cpol.purchase_quantity - avg = cpol.average_consumption + avg = cpol.daily_sales if avg > 0: qty = ((cpol.virtual_available / cpol.uom_id.factor) + (cpol.purchase_quantity / cpol.uom_po_id.factor)) diff --git a/compute_purchase_order/views/computed_purchase_order.xml b/compute_purchase_order/views/computed_purchase_order.xml index fe9efdd..947a749 100644 --- a/compute_purchase_order/views/computed_purchase_order.xml +++ b/compute_purchase_order/views/computed_purchase_order.xml @@ -60,7 +60,7 @@ - + diff --git a/compute_purchase_order/views/product_template.xml b/compute_purchase_order/views/product_template.xml index c5607c8..a600743 100644 --- a/compute_purchase_order/views/product_template.xml +++ b/compute_purchase_order/views/product_template.xml @@ -12,10 +12,10 @@ - + - - + + From 33cbede8aedf0d87d5769c457ad4761d729ef843 Mon Sep 17 00:00:00 2001 From: Vincent Van Rossem Date: Thu, 16 Apr 2020 15:36:01 +0200 Subject: [PATCH 14/21] [FIX] compute_purchase_order: product.uom -> uom.uom --- compute_purchase_order/models/computed_purchase_order_line.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compute_purchase_order/models/computed_purchase_order_line.py b/compute_purchase_order/models/computed_purchase_order_line.py index a0f3882..4bdead9 100644 --- a/compute_purchase_order/models/computed_purchase_order_line.py +++ b/compute_purchase_order/models/computed_purchase_order_line.py @@ -41,7 +41,7 @@ class ComputedPurchaseOrderLine(models.Model): read_only=True) uom_id = fields.Many2one( - 'product.uom', + 'uom.uom', string='Unit of Measure', read_only=True, related='product_template_id.uom_id', @@ -83,7 +83,7 @@ class ComputedPurchaseOrderLine(models.Model): default=0.) uom_po_id = fields.Many2one( - 'product.uom', + 'uom.uom', string='Purchase Unit of Measure', read_only=True, related='product_template_id.uom_po_id', From 73fa87b341a0654a57b52579f2785945217f9093 Mon Sep 17 00:00:00 2001 From: Vincent Van Rossem Date: Thu, 16 Apr 2020 17:10:58 +0200 Subject: [PATCH 15/21] [FIX] computed_purchase_order: bad sql query - duplicated id --- compute_purchase_order/security/ir.model.access.csv | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/compute_purchase_order/security/ir.model.access.csv b/compute_purchase_order/security/ir.model.access.csv index faf22c8..982809e 100644 --- a/compute_purchase_order/security/ir.model.access.csv +++ b/compute_purchase_order/security/ir.model.access.csv @@ -6,7 +6,6 @@ access_purchase_order_stock_worker,purchase.order,model_computed_purchase_order, access_purchase_order_invoicing_payments,purchase.order,model_computed_purchase_order,account.group_account_invoice,1,1,0,0 access_purchase_order_line,purchase.order.line user,model_computed_purchase_order_line,purchase.group_purchase_user,1,1,1,1 -access_purchase_order_line_manager,purchase.order.line manager,model_computed_purchase_order_line,purchase.group_purchase_manager,1,0,0,0 +access_purchase_order_line_manager,purchase.order.line manager,model_computed_purchase_order_line,purchase.group_purchase_manager,1,1,1,1 access_purchase_order_line_stock_worker,purchase.order.line,model_computed_purchase_order_line,stock.group_stock_user,1,0,0,0 -access_purchase_order_line_manager,purchase.order.line,model_computed_purchase_order_line,purchase.group_purchase_manager,1,1,1,1 access_purchase_order_line_invoicing_payments,purchase.order.line,model_computed_purchase_order_line,account.group_account_invoice,1,1,0,0 From ff9959c7253b83e948c2d3f519bf835f9d2ac8f1 Mon Sep 17 00:00:00 2001 From: Vincent Van Rossem Date: Thu, 16 Apr 2020 17:13:11 +0200 Subject: [PATCH 16/21] [TOFIX] computed_purchase_order: cpo_seller_id is missing --- compute_purchase_order/views/computed_purchase_order.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compute_purchase_order/views/computed_purchase_order.xml b/compute_purchase_order/views/computed_purchase_order.xml index 947a749..bd888a9 100644 --- a/compute_purchase_order/views/computed_purchase_order.xml +++ b/compute_purchase_order/views/computed_purchase_order.xml @@ -32,7 +32,7 @@ - + @@ -54,9 +54,9 @@ - + - + From 1d644f4d9d5404febd17f5afaa313e39a394536d Mon Sep 17 00:00:00 2001 From: "robin.keunen" Date: Mon, 27 Apr 2020 16:23:42 +0200 Subject: [PATCH 17/21] [ADD] cpo: readme --- compute_purchase_order/README.rst | 58 +++ compute_purchase_order/__manifest__.py | 38 +- .../readme/CONTRIBUTORS.rst | 2 + compute_purchase_order/readme/DESCRIPTION.rst | 1 + compute_purchase_order/readme/USAGE.rst | 0 .../static/description/index.html | 415 ++++++++++++++++++ 6 files changed, 497 insertions(+), 17 deletions(-) create mode 100644 compute_purchase_order/README.rst create mode 100644 compute_purchase_order/readme/CONTRIBUTORS.rst create mode 100644 compute_purchase_order/readme/DESCRIPTION.rst create mode 100644 compute_purchase_order/readme/USAGE.rst create mode 100644 compute_purchase_order/static/description/index.html diff --git a/compute_purchase_order/README.rst b/compute_purchase_order/README.rst new file mode 100644 index 0000000..f8e0c96 --- /dev/null +++ b/compute_purchase_order/README.rst @@ -0,0 +1,58 @@ +======================= +Computed Purchase Order +======================= + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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 +.. |badge3| image:: https://img.shields.io/badge/github-beescoop%2Fobeesdoo-lightgray.png?logo=github + :target: https://github.com/beescoop/obeesdoo/tree/12.0/compute_purchase_order + :alt: beescoop/obeesdoo + +|badge1| |badge2| |badge3| + +Compute purchase order from selected products. + +**Table of contents** + +.. contents:: + :local: + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* Coop IT Easy + +Contributors +~~~~~~~~~~~~ + +* Robin Keunen +* Vincent Van Rossem + +Maintainers +~~~~~~~~~~~ + +This module is part of the `beescoop/obeesdoo `_ project on GitHub. + +You are welcome to contribute. diff --git a/compute_purchase_order/__manifest__.py b/compute_purchase_order/__manifest__.py index d7f1427..e944a98 100644 --- a/compute_purchase_order/__manifest__.py +++ b/compute_purchase_order/__manifest__.py @@ -1,20 +1,24 @@ +# Copyright 2020 Coop IT Easy SCRL fs +# Robin Keunen +# Vincent Van Rossem +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). { - 'name': 'Computed Purchase Order', - 'version': '12.0.1.0.0', - 'category': 'Purchase Order', - 'description': """ todo """, - 'author': 'Coop IT Easy', - 'website': 'https://github.com/coopiteasy/procurement-addons', - 'license': 'AGPL-3', - 'depends': [ - 'product', - 'purchase', - 'stock', - 'beesdoo_stock_coverage', - ], - 'data': [ - 'security/ir.model.access.csv', - 'views/computed_purchase_order.xml', - 'views/purchase_order.xml', + "name": "Computed Purchase Order", + "version": "12.0.1.0.0", + "category": "Purchase Order", + "summary": "Compute purchase order from selected products", + "author": "Coop IT Easy", + "website": "https://github.com/coopiteasy/procurement-addons", + "license": "AGPL-3", + "depends": [ + "product", + "purchase", + "stock", + "beesdoo_stock_coverage", + ], # todo simplify + "data": [ + "security/ir.model.access.csv", + "views/computed_purchase_order.xml", + "views/purchase_order.xml", ], } diff --git a/compute_purchase_order/readme/CONTRIBUTORS.rst b/compute_purchase_order/readme/CONTRIBUTORS.rst new file mode 100644 index 0000000..4eeddcc --- /dev/null +++ b/compute_purchase_order/readme/CONTRIBUTORS.rst @@ -0,0 +1,2 @@ +* Robin Keunen +* Vincent Van Rossem diff --git a/compute_purchase_order/readme/DESCRIPTION.rst b/compute_purchase_order/readme/DESCRIPTION.rst new file mode 100644 index 0000000..e9f66f3 --- /dev/null +++ b/compute_purchase_order/readme/DESCRIPTION.rst @@ -0,0 +1 @@ +Compute purchase order from selected products. diff --git a/compute_purchase_order/readme/USAGE.rst b/compute_purchase_order/readme/USAGE.rst new file mode 100644 index 0000000..e69de29 diff --git a/compute_purchase_order/static/description/index.html b/compute_purchase_order/static/description/index.html new file mode 100644 index 0000000..699639d --- /dev/null +++ b/compute_purchase_order/static/description/index.html @@ -0,0 +1,415 @@ + + + + + + +Computed Purchase Order + + + +
+

Computed Purchase Order

+ + +

Beta License: AGPL-3 beescoop/obeesdoo

+

Compute purchase order from selected products.

+

Table of contents

+ +
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • Coop IT Easy
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is part of the beescoop/obeesdoo project on GitHub.

+

You are welcome to contribute.

+
+
+
+ + From 4e58913059c882beef04184c255f4a02a25cab20 Mon Sep 17 00:00:00 2001 From: "robin.keunen" Date: Mon, 27 Apr 2020 15:37:13 +0200 Subject: [PATCH 18/21] [REF] cpo: refactor [REF] cpo: blacken wip --- compute_purchase_order/__manifest__.py | 7 +- .../models/computed_purchase_order.py | 303 +++++++----------- .../models/computed_purchase_order_line.py | 256 +++++++-------- .../models/product_template.py | 26 +- .../models/purchase_order.py | 42 ++- compute_purchase_order/tests/__init__.py | 1 + compute_purchase_order/tests/test_cpo.py | 84 +++++ .../views/computed_purchase_order.xml | 211 ++++++------ 8 files changed, 489 insertions(+), 441 deletions(-) create mode 100644 compute_purchase_order/tests/__init__.py create mode 100644 compute_purchase_order/tests/test_cpo.py diff --git a/compute_purchase_order/__manifest__.py b/compute_purchase_order/__manifest__.py index e944a98..8a9de1e 100644 --- a/compute_purchase_order/__manifest__.py +++ b/compute_purchase_order/__manifest__.py @@ -10,12 +10,7 @@ "author": "Coop IT Easy", "website": "https://github.com/coopiteasy/procurement-addons", "license": "AGPL-3", - "depends": [ - "product", - "purchase", - "stock", - "beesdoo_stock_coverage", - ], # todo simplify + "depends": ["purchase", "beesdoo_stock_coverage"], "data": [ "security/ir.model.access.csv", "views/computed_purchase_order.xml", diff --git a/compute_purchase_order/models/computed_purchase_order.py b/compute_purchase_order/models/computed_purchase_order.py index 9df5623..9398e0d 100644 --- a/compute_purchase_order/models/computed_purchase_order.py +++ b/compute_purchase_order/models/computed_purchase_order.py @@ -1,228 +1,167 @@ -from odoo import models, fields, api, SUPERUSER_ID +# Copyright 2020 Coop IT Easy SCRL fs +# Robin Keunen +# Vincent Van Rossem +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from odoo import models, fields, api, _ from odoo.exceptions import ValidationError class ComputedPurchaseOrder(models.Model): - _description = 'Computed Purchase Order' - _name = 'computed.purchase.order' - _order = 'id desc' - - name = fields.Char( - string='CPO Reference', - size=64, - default='New') + _description = "Computed Purchase Order" + _name = "computed.purchase.order" + _order = "id desc" + name = fields.Char(string="CPO Reference", default=_("New")) order_date = fields.Datetime( - string='Purchase Order Date', + string="Purchase Order Date", default=fields.Datetime.now, - help="Depicts the date where the Quotation should be validated and converted into a purchase order.") # noqa - + help="Date at which the Quotation should be validated and " + "converted into a purchase order.", + ) date_planned = fields.Datetime( - string='Date Planned' + string="Date Planned", + default=fields.Datetime.now, # default=lambda _: fields.Datetime.now() ) - supplier_id = fields.Many2one( - 'res.partner', - string='Supplier', + comodel_name="res.partner", + string="Supplier", readonly=True, - help="Supplier of the purchase order.") - - order_line_ids = fields.One2many( - 'computed.purchase.order.line', - 'computed_purchase_order_id', - string='Order Lines', + help="Supplier of the purchase order.", + ) + cpo_line_ids = fields.One2many( + comodel_name="computed.purchase.order.line", + inverse_name="cpo_id", + string="Order Lines", ) - total_amount = fields.Float( - string='Total Amount (w/o VAT)', - compute='_compute_cpo_total', - store=True, + string="Total Amount (w/o VAT)", compute="_compute_cpo_total" ) - generated_purchase_order_ids = fields.One2many( - 'purchase.order', - 'original_cpo_id', - string='Generated Purchase Orders', + comodel_name="purchase.order", + inverse_name="original_cpo_id", + string="Generated Purchase Orders", ) - generated_po_count = fields.Integer( - string='Generated Purchase Order count', - compute='_compute_generated_po_count', + string="Generated Purchase Order count", + compute="_compute_generated_po_count", ) + @api.multi + @api.depends("cpo_line_ids", "cpo_line_ids.purchase_quantity") + def _compute_cpo_total(self): + for cpo in self: + total_amount = sum(cpol.subtotal for cpol in cpo.cpo_line_ids) + cpo.total_amount = total_amount + @api.model - def default_get(self, fields_list): - record = super(ComputedPurchaseOrder, self).default_get(fields_list) - - record['date_planned'] = self._get_default_date_planned() - record['supplier_id'] = self._get_selected_supplier_id() - record['order_line_ids'] = self._create_order_lines() - record['name'] = self._compute_default_name() - - return record - - def _get_default_date_planned(self): - return fields.Datetime.now() - - def _get_selected_supplier_id(self): - """ - Calcule le vendeur associé qui a la date de début la plus récente et - plus petite qu’aujourd’hui pour chaque article sélectionné. - Will raise an error if more than two sellers are set - """ - if 'active_ids' not in self.env.context: - return False - - product_ids = self.env.context['active_ids'] - products = self.env['product.template'].browse(product_ids) - - suppliers = set() - for product in products: - main_supplier_id = product.main_supplier_id.id - suppliers.add(main_supplier_id) - - if len(suppliers) == 0: - raise ValidationError('No supplier is set for selected articles.') + def _get_selected_supplier(self): + product_ids = self.env.context.get("active_ids", []) + products = self.env["product.template"].browse(product_ids) + suppliers = products.mapped("main_supplier_id") + + if not suppliers: + raise ValidationError("No supplier is set for selected articles.") elif len(suppliers) == 1: - return suppliers.pop() + return suppliers else: raise ValidationError( - 'You must select article from a single supplier.') - - def _create_order_lines(self): - product_tmpl_ids = self._get_selected_products() - cpol_ids = [] - OrderLine = self.env['computed.purchase.order.line'] - for product_id in product_tmpl_ids: - cpol = OrderLine.create( - {'computed_purchase_order_id': self.id, - 'product_template_id': product_id, - } + "You must select article from a single supplier." ) - # should ideally be set in cpol defaults - cpol.purchase_quantity = cpol.minimum_purchase_qty - cpol_ids.append(cpol.id) - return cpol_ids - - def _compute_default_name(self): - supplier_id = self._get_selected_supplier_id() - if supplier_id: - supplier_name = ( - self.env['res.partner'] - .browse(supplier_id) - .name) - - name = 'CPO {} {}'.format( - supplier_name, - fields.Date.today()) - else: - name = 'New' - return name - - def _get_selected_products(self): - if 'active_ids' in self.env.context: - return self.env.context['active_ids'] - else: - return [] - - @api.multi - def _compute_generated_po_count(self): - for cpo in self: - cpo.generated_po_count = len(cpo.generated_purchase_order_ids) - @api.multi - def get_generated_po_action(self): - self.ensure_one() + @api.model + def generate_cpo(self): + order_line_obj = self.env["computed.purchase.order.line"] + product_ids = self.env.context.get("active_ids", []) + + supplier = self._get_selected_supplier() + name = "CPO {} {}".format(supplier.name, fields.Date.today()) + cpo = self.create({"name": name, "supplier_id": supplier.id}) + + for product_id in product_ids: + supplierinfo = self.env["product.supplierinfo"].search( + [ + ("product_tmpl_id", "=", product_id), + ("name", "=", supplier.id), + ] + ) + min_qty = supplierinfo.min_qty if supplierinfo else 0 + order_line_obj.create( + { + "cpo_id": cpo.id, + "product_template_id": product_id, + "purchase_quantity": min_qty, + } + ) action = { - 'type': 'ir.actions.act_window', - 'res_model': 'purchase.order', - 'view_mode': 'tree,form,kanban', - 'target': 'current', - 'domain': [('id', 'in', self.generated_purchase_order_ids.ids)], + "type": "ir.actions.act_window", + "res_model": "computed.purchase.order", + "views": [[False, "form"]], + # "view_mode": "form,tree", + # "view_type": "form", + # "target": "current", + "res_id": cpo.id, } return action - @api.depends('order_line_ids.subtotal') - @api.multi - def _compute_cpo_total(self): - for cpo in self: - total_amount = sum(cpol.subtotal for cpol in cpo.order_line_ids) - cpo.total_amount = total_amount - @api.multi def create_purchase_order(self): self.ensure_one() - if sum(self.order_line_ids.mapped('purchase_quantity')) == 0: - raise ValidationError('You need at least a product to generate ' - 'a Purchase Order') + if sum(self.cpo_line_ids.mapped("purchase_quantity")) == 0: + raise ValidationError( + "You need at least a product to generate " "a Purchase Order" + ) - PurchaseOrder = self.env['purchase.order'] - PurchaseOrderLine = self.env['purchase.order.line'] + purchase_order = self.env["purchase.order"].create( + { + "date_order": self.order_date, + "partner_id": self.supplier_id.id, + "date_planned": self.date_planned, + } + ) - po_values = { - 'name': 'New', - 'date_order': self.order_date, - 'partner_id': self.supplier_id.id, - 'date_planned': self.date_planned, - } - purchase_order = PurchaseOrder.create(po_values) - - for cpo_line in self.order_line_ids: + for cpo_line in self.cpo_line_ids: if cpo_line.purchase_quantity > 0: - if cpo_line.supplierinfo_id.product_code: - pol_name = '[%s] %s' % (cpo_line.supplierinfo_id.product_code, cpo_line.name) - else: - pol_name = cpo_line.name - pol_values = { - 'name': pol_name, - 'product_id': cpo_line.get_default_product_product().id, - 'product_qty': cpo_line.purchase_quantity, - 'price_unit': cpo_line.product_price, - 'product_uom': cpo_line.uom_po_id.id, - 'order_id': purchase_order.id, - 'date_planned': self.date_planned, - } - pol = PurchaseOrderLine.create(pol_values) + pol = self.env["purchase.order.line"].create( + { + "name": cpo_line.name, + "product_id": cpo_line.product_template_id.product_variant_id.id, + "product_qty": cpo_line.purchase_quantity, + "price_unit": cpo_line.product_price, + "product_uom": cpo_line.uom_po_id.id, + "order_id": purchase_order.id, + "date_planned": self.date_planned, + } + ) pol.compute_taxes_id() self.generated_purchase_order_ids += purchase_order action = { - 'type': 'ir.actions.act_window', - 'res_model': 'purchase.order', - 'res_id': purchase_order.id, - 'view_type': 'form', - 'view_mode': 'form', - 'target': 'current', + "type": "ir.actions.act_window", + "res_model": "purchase.order", + "res_id": purchase_order.id, + "view_type": "form", + "view_mode": "form,tree", + "target": "current", } return action - -class PurchaseOrderLine(models.Model): - _inherit = 'purchase.order.line' - @api.multi - def compute_taxes_id(self): - for pol in self: - if self.env.uid == SUPERUSER_ID: - company_id = self.env.user.company_id.id - else: - company_id = self.company_id.id - - fpos_id = ( - self.env['account.fiscal.position'] - .with_context(company_id=company_id) - .get_fiscal_position(pol.partner_id.id) - ) - fpos = self.env['account.fiscal.position'].browse(fpos_id) - pol.order_id.fiscal_position_id = fpos - - taxes = self.product_id.supplier_taxes_id - taxes_id = fpos.map_tax(taxes) if fpos else taxes - - if taxes_id: - taxes_id = taxes_id.filtered( - lambda t: t.company_id.id == company_id) + @api.depends("generated_purchase_order_ids") + def _compute_generated_po_count(self): + for cpo in self: + cpo.generated_po_count = len(cpo.generated_purchase_order_ids) - pol.taxes_id = taxes_id + @api.multi + def get_generated_po_action(self): + self.ensure_one() + action = { + "type": "ir.actions.act_window", + "res_model": "purchase.order", + "view_mode": "tree,form,kanban", + "target": "current", + "domain": [("id", "in", self.generated_purchase_order_ids.ids)], + } + return action diff --git a/compute_purchase_order/models/computed_purchase_order_line.py b/compute_purchase_order/models/computed_purchase_order_line.py index 4bdead9..ab97a90 100644 --- a/compute_purchase_order/models/computed_purchase_order_line.py +++ b/compute_purchase_order/models/computed_purchase_order_line.py @@ -1,132 +1,128 @@ +# Copyright 2020 Coop IT Easy SCRL fs +# Robin Keunen +# Vincent Van Rossem +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). import logging -from odoo import models, fields, api +from odoo import models, fields, api, _ from odoo.exceptions import ValidationError _logger = logging.getLogger(__name__) class ComputedPurchaseOrderLine(models.Model): - _description = 'Computed Purchase Order Line' - _name = 'computed.purchase.order.line' + _description = "Computed Purchase Order Line" + _name = "computed.purchase.order.line" - computed_purchase_order_id = fields.Many2one( - 'computed.purchase.order', - string='Computed Purchase Order', + name = fields.Char(string="Product Name", compute="_compute_name") + cpo_id = fields.Many2one( + comodel_name="computed.purchase.order", + string="Computed Purchase Order", ) - product_template_id = fields.Many2one( - 'product.template', - string='Linked Product Template', + comodel_name="product.template", + string="Linked Product Template", required=True, - help='Product') - - name = fields.Char( - string='Product Name', - related='product_template_id.name', - read_only=True) - - supplierinfo_id = fields.Many2one( - 'product.supplierinfo', - string='Supplier information', - compute='_compute_supplierinfo', - store=True, - readonly=True, + help="Product", ) - + purchase_quantity = fields.Float(string="Purchase Quantity", default=0.0) category_id = fields.Many2one( - 'product.category', - string='Internal Category', - related='product_template_id.categ_id', - read_only=True) - + comodel_name="product.category", + string="Internal Category", + related="product_template_id.categ_id", + read_only=True, + ) uom_id = fields.Many2one( - 'uom.uom', - string='Unit of Measure', + comodel_name="uom.uom", + string="Unit of Measure", read_only=True, - related='product_template_id.uom_id', - help="Default Unit of Measure used for all stock operation.") - + related="product_template_id.uom_id", + help="Default Unit of Measure used for all stock operation.", + ) qty_available = fields.Float( - string='Stock Quantity', - related='product_template_id.qty_available', + string="Stock Quantity", + related="product_template_id.qty_available", read_only=True, - help='Quantity currently in stock. Does not take ' - 'into account incoming orders.') - + help="Quantity currently in stock. Does not take " + "into account incoming orders.", + ) virtual_available = fields.Float( - string='Forecast Quantity', - related='product_template_id.virtual_available', + string="Forecast Quantity", + related="product_template_id.virtual_available", read_only=True, - help='Virtual quantity taking into account current stock, incoming ' - 'orders and outgoing sales.') - + help="Virtual quantity taking into account current stock, incoming " + "orders and outgoing sales.", + ) daily_sales = fields.Float( - string='Average Consumption', - related='product_template_id.daily_sales', - read_only=True) - - stock_coverage = fields.Float( - string='Stock Coverage', - related='product_template_id.stock_coverage', + string="Average Consumption", + related="product_template_id.daily_sales", read_only=True, ) - - minimum_purchase_qty = fields.Float( - string='Minimum Purchase Quantity', - compute='_depends_on_product_template', + stock_coverage = fields.Float( + string="Stock Coverage", + related="product_template_id.stock_coverage", + read_only=True, ) - - purchase_quantity = fields.Float( - string='Purchase Quantity', - required=True, - default=0.) - uom_po_id = fields.Many2one( - 'uom.uom', - string='Purchase Unit of Measure', + comodel_name="uom.uom", + string="Purchase Unit of Measure", read_only=True, - related='product_template_id.uom_po_id', - help="Default Unit of Measure used for all stock operation.") - + related="product_template_id.uom_po_id", + help="Default Unit of Measure used for all stock operation.", + ) + supplierinfo_id = fields.Many2one( + comodel_name="product.supplierinfo", + string="Supplier information", + compute="_compute_supplierinfo", + store=True, + readonly=True, + ) + minimum_purchase_qty = fields.Float( + string="Minimum Purchase Quantity", related="supplierinfo_id.min_qty" + ) product_price = fields.Float( - string='Product Price (w/o VAT)', - compute='_depends_on_product_template', - read_only=True, - help='Supplier Product Price by buying unit. Price is without VAT') - + string="Product Price (w/o VAT)", + related="supplierinfo_id.price", + help="Supplier Product Price by buying unit. Price is without VAT", + ) virtual_coverage = fields.Float( - string='Expected Stock Coverage', - compute='_depends_on_purchase_quantity', - help='Expected stock coverage (in days) based on current stocks and average daily consumption') # noqa - + string="Expected Stock Coverage", + compute="_compute_coverage_and_subtotal", + help="Expected stock coverage (in days) based on current stocks and " + "average daily consumption", + ) subtotal = fields.Float( - string='Subtotal (w/o VAT)', - compute='_depends_on_purchase_quantity') + string="Subtotal (w/o VAT)", compute="_compute_coverage_and_subtotal" + ) @api.multi - @api.depends('product_template_id') - def _depends_on_product_template(self): + @api.depends("supplierinfo_id") + def _compute_name(self): for cpol in self: - # get supplier info - cpol.minimum_purchase_qty = cpol.supplierinfo_id.min_qty - cpol.product_price = cpol.supplierinfo_id.price + if cpol.supplierinfo_id and cpol.supplierinfo_id.product_code: + product_code = cpol.supplierinfo_id.product_code + product_name = cpol.product_template_id.name + cpol_name = "[%s] %s" % (product_code, product_name) + else: + cpol_name = cpol.product_template_id.name + cpol.name = cpol_name @api.multi - @api.onchange('product_template_id') + @api.onchange("product_template_id") def _onchange_purchase_quantity(self): for cpol in self: - cpol.purchase_quantity = cpol.supplierinfo_id.min_qty + cpol.purchase_quantity = cpol.minimum_purchase_qty - @api.depends('purchase_quantity') @api.multi - def _depends_on_purchase_quantity(self): + @api.depends("purchase_quantity") + def _compute_coverage_and_subtotal(self): for cpol in self: cpol.subtotal = cpol.product_price * cpol.purchase_quantity avg = cpol.daily_sales if avg > 0: - qty = ((cpol.virtual_available / cpol.uom_id.factor) - + (cpol.purchase_quantity / cpol.uom_po_id.factor)) + qty = (cpol.virtual_available / cpol.uom_id.factor) + ( + cpol.purchase_quantity / cpol.uom_po_id.factor + ) cpol.virtual_coverage = qty / avg else: # todo what would be a good default value? (not float(inf)) @@ -135,61 +131,51 @@ class ComputedPurchaseOrderLine(models.Model): return True @api.multi - @api.depends('product_template_id') - @api.onchange('product_template_id') + @api.depends("product_template_id") def _compute_supplierinfo(self): for cpol in self: if not cpol.product_template_id: - cpol.supplierinfo_id = False - else: - SupplierInfo = self.env['product.supplierinfo'] - si = SupplierInfo.search([ - ('product_tmpl_id', '=', cpol.product_template_id.id), - ('name', '=', cpol.product_template_id.main_supplier_id.id) # noqa - ]) + continue - if len(si) == 0: - raise ValidationError( - 'No supplier information set for {name}' - .format(name=cpol.product_template_id.name)) - elif len(si) == 1: - cpol.supplierinfo_id = si - else: - _logger.warning( - 'product {name} has several suppliers, chose last' - .format(name=cpol.product_template_id.name) + si = self.env["product.supplierinfo"].search( + [ + ("product_tmpl_id", "=", cpol.product_template_id.id), + ("name", "=", cpol.cpo_id.supplier_id.id), + ] + ) + + if len(si) == 0: + raise ValidationError( + _("CPO supplier does not sell product {name}").format( + name=cpol.product_template_id.name + ) + ) + elif len(si) > 1: + _logger.warning( + "product {name} has several supplier info set, chose last".format( + name=cpol.product_template_id.name ) - si = si.sorted(key=lambda r: r.create_date, reverse=True) - cpol.supplierinfo_id = si[0] + ) + si = si.sorted(key=lambda r: r.create_date, reverse=True) + cpol.supplierinfo_id = si[0] - @api.constrains('purchase_quantity') + @api.constrains("purchase_quantity") def _check_minimum_purchase_quantity(self): for cpol in self: if cpol.purchase_quantity < 0: - raise ValidationError('Purchase quantity for {product_name} must be greater than 0' # noqa - .format(product_name=cpol.product_template_id.name)) + raise ValidationError( + _( + "Purchase quantity for {product_name} " + "must be greater than 0" + ).format(product_name=cpol.product_template_id.name) + ) elif 0 < cpol.purchase_quantity < cpol.minimum_purchase_qty: - raise ValidationError('Purchase quantity for {product_name} must be greater than {min_qty}' # noqa - .format(product_name=cpol.product_template_id.name, - min_qty=cpol.minimum_purchase_qty)) - - @api.multi - def get_default_product_product(self): - self.ensure_one() - ProductProduct = self.env['product.product'] - products = ProductProduct.search([ - ('product_tmpl_id', '=', self.product_template_id.id) - ]) - - products = products.sorted( - key=lambda product: product.create_date, - reverse=True - ) - - if products: - return products[0] - else: - raise ValidationError( - '%s:%s template has no variant set' - % (self.product_template_id.id, self.product_template_id.name) - ) + raise ValidationError( + _( + "Purchase quantity for {product_name} " + "must be greater than {min_qty}" + ).format( + product_name=cpol.product_template_id.name, + min_qty=cpol.minimum_purchase_qty, + ) + ) diff --git a/compute_purchase_order/models/product_template.py b/compute_purchase_order/models/product_template.py index 61bf5e9..5cd0545 100644 --- a/compute_purchase_order/models/product_template.py +++ b/compute_purchase_order/models/product_template.py @@ -1,3 +1,8 @@ +# Copyright 2020 Coop IT Easy SCRL fs +# Robin Keunen +# Vincent Van Rossem +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + from odoo import models, fields, api @@ -5,21 +10,22 @@ class ProductTemplate(models.Model): _inherit = "product.template" main_supplier_id = fields.Many2one( - 'res.partner', - compute='_compute_main_supplier_id', - store=True + "res.partner", compute="_compute_main_supplier_id", store=True ) def _get_sorted_supplierinfo(self): return self.seller_ids.sorted( - key=lambda seller: seller.date_start, - reverse=True) + key=lambda seller: seller.date_start, reverse=True + ) @api.multi - @api.depends('seller_ids', 'seller_ids.date_start') + @api.depends("seller_ids", "seller_ids.date_start") def _compute_main_supplier_id(self): - # Calcule le vendeur associé qui a la date de début la plus récente - # et plus petite qu’aujourd’hui for pt in self: - sellers_ids = pt._get_sorted_supplierinfo() - pt.main_supplier_id = sellers_ids and sellers_ids[0].name or False + sellers_ids = pt.seller_ids.sorted( + key=lambda seller: seller.date_start, reverse=True + ) + if sellers_ids: + pt.main_supplier_id = sellers_ids[0].name + else: + pt.main_supplier_id = False diff --git a/compute_purchase_order/models/purchase_order.py b/compute_purchase_order/models/purchase_order.py index 2c57db6..c72b2be 100644 --- a/compute_purchase_order/models/purchase_order.py +++ b/compute_purchase_order/models/purchase_order.py @@ -1,11 +1,45 @@ -from odoo import models, fields +# Copyright 2020 Coop IT Easy SCRL fs +# Robin Keunen +# Vincent Van Rossem +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +from odoo import models, fields, api, SUPERUSER_ID class PurchaseOrder(models.Model): _inherit = "purchase.order" original_cpo_id = fields.Many2one( - 'computed.purchase.order', - string='Original CPO', - help='CPO used to generate this Purchase Order' + "computed.purchase.order", + string="Original CPO", + help="CPO used to generate this Purchase Order", ) + + +class PurchaseOrderLine(models.Model): + _inherit = "purchase.order.line" + + @api.multi + def compute_taxes_id(self): + for pol in self: + if self.env.uid == SUPERUSER_ID: + company_id = self.env.user.company_id.id + else: + company_id = self.company_id.id + + fpos_id = ( + self.env["account.fiscal.position"] + .with_context(company_id=company_id) + .get_fiscal_position(pol.partner_id.id) + ) + fpos = self.env["account.fiscal.position"].browse(fpos_id) + pol.order_id.fiscal_position_id = fpos + + taxes = self.product_id.supplier_taxes_id + taxes_id = fpos.map_tax(taxes) if fpos else taxes + + if taxes_id: + taxes_id = taxes_id.filtered( + lambda t: t.company_id.id == company_id + ) + + pol.taxes_id = taxes_id diff --git a/compute_purchase_order/tests/__init__.py b/compute_purchase_order/tests/__init__.py new file mode 100644 index 0000000..0246a73 --- /dev/null +++ b/compute_purchase_order/tests/__init__.py @@ -0,0 +1 @@ +from . import test_cpo diff --git a/compute_purchase_order/tests/test_cpo.py b/compute_purchase_order/tests/test_cpo.py new file mode 100644 index 0000000..c50830b --- /dev/null +++ b/compute_purchase_order/tests/test_cpo.py @@ -0,0 +1,84 @@ +# Copyright (C) 2019 - Today: GRAP (http://www.grap.coop) +# @author: Robin Keunen +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo.tests.common import TransactionCase, Form + + +class TestCPO(TransactionCase): + def setUp(self): + super().setUp() + + self.supplier = self.browse_ref("base.res_partner_1") + self.pproduct1 = self.browse_ref("product.product_product_25") + self.ptemplate1 = self.pproduct1.product_tmpl_id + self.pproduct2 = self.browse_ref("product.product_delivery_02") + self.ptemplate2 = self.pproduct2.product_tmpl_id + + def test_generate_cpo(self): + supplierinfo_obj = self.env["product.supplierinfo"] + supplierinfo = supplierinfo_obj.search( + [ + ("name", "=", self.supplier.id), + ("product_tmpl_id", "=", self.ptemplate1.id), + ] + ) + supplierinfo2 = supplierinfo_obj.search( + [ + ("name", "=", self.supplier.id), + ("product_tmpl_id", "=", self.ptemplate2.id), + ] + ) + + cpo_obj = self.env["computed.purchase.order"] + cpo_action = cpo_obj.with_context( + active_ids=[self.ptemplate1.id] + ).generate_cpo() + cpo = cpo_obj.browse(cpo_action["res_id"]) + cpol = cpo.cpo_line_ids # expect one line + + self.assertEquals(cpo.supplier_id, self.supplier) + self.assertEquals(cpol.product_template_id, self.ptemplate1) + self.assertEquals(cpol.product_price, supplierinfo.price) + self.assertEquals(cpol.purchase_quantity, supplierinfo.min_qty) + + # testing triggers + expected_subtotal = supplierinfo.price * supplierinfo.min_qty + self.assertEquals(cpol.subtotal, expected_subtotal) + + cpol.purchase_quantity = 4 + expected_subtotal = supplierinfo.price * 4 + self.assertEquals(cpol.subtotal, expected_subtotal) + + cpo_form = Form(cpo) + with cpo_form.cpo_line_ids.edit(index=0) as line_form: + line_form.product_template_id = self.ptemplate2 + self.assertEquals(line_form.product_template_id, self.ptemplate2) + cpo = cpo_form.save() + cpol = cpo.cpo_line_ids + + expected_subtotal = supplierinfo2.price * supplierinfo2.min_qty + self.assertEquals(cpol.product_price, supplierinfo2.price) + self.assertEquals(cpol.purchase_quantity, supplierinfo2.min_qty) + self.assertEquals(cpol.subtotal, expected_subtotal) + + def test_generate_po(self): + cpo_obj = self.env["computed.purchase.order"] + cpo_action = cpo_obj.with_context( + active_ids=[self.ptemplate1.id, self.ptemplate2.id] + ).generate_cpo() + cpo = cpo_obj.browse(cpo_action["res_id"]) + po_action = cpo.create_purchase_order() + po = self.env["purchase.order"].browse(po_action["res_id"]) + + self.assertEquals(cpo.supplier_id, po.partner_id) + self.assertEquals(len(cpo.cpo_line_ids), len(po.order_line)) + lines = zip( + cpo.cpo_line_ids.sorted(lambda l: l.product_template_id), + po.order_line.sorted(lambda l: l.product_id.product_tmpl_id), + ) + for cpol, pol in lines: + self.assertEquals( + cpol.product_template_id, pol.product_id.product_tmpl_id + ) + self.assertEquals(cpol.purchase_quantity, pol.product_qty) diff --git a/compute_purchase_order/views/computed_purchase_order.xml b/compute_purchase_order/views/computed_purchase_order.xml index bd888a9..f4c9b64 100644 --- a/compute_purchase_order/views/computed_purchase_order.xml +++ b/compute_purchase_order/views/computed_purchase_order.xml @@ -1,115 +1,118 @@ - + - - computed.purchase.order.tree - computed.purchase.order - - - - - - - - - - + + computed.purchase.order.tree + computed.purchase.order + + + + + + + + + + - - computed.purchase.order.form - computed.purchase.order - -
-
-
- - - - - - - - - - - -
- -
- -
- - - - - - - - - - - - - - - - -
- - -
-
-
- - - - computed.purchase.order - - - - - - - - - - - Computed Purchase Orders - ir.actions.act_window - computed.purchase.order - tree,form - - - - + + computed.purchase.order.form + computed.purchase.order + +
+
+
+ + + + + + + + + + + +
+ +
+ +
+ + + + + + + + + + + + + + + + +
+ + +
+
+
+ + + + computed.purchase.order + + + + + + + + + + + Computed Purchase Orders + computed.purchase.order + + + - + + Compute Purchase Order + + + code + + model.generate_cpo() + + + +
From fbecfa5ba62a916a4360fee5817c4d3d3d66442a Mon Sep 17 00:00:00 2001 From: "robin.keunen" Date: Wed, 29 Apr 2020 16:49:06 +0200 Subject: [PATCH 19/21] [IMP] cpo: return act_window from server action --- compute_purchase_order/models/computed_purchase_order.py | 7 +++---- compute_purchase_order/views/computed_purchase_order.xml | 7 +------ 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/compute_purchase_order/models/computed_purchase_order.py b/compute_purchase_order/models/computed_purchase_order.py index 9398e0d..48766db 100644 --- a/compute_purchase_order/models/computed_purchase_order.py +++ b/compute_purchase_order/models/computed_purchase_order.py @@ -96,11 +96,10 @@ class ComputedPurchaseOrder(models.Model): action = { "type": "ir.actions.act_window", "res_model": "computed.purchase.order", - "views": [[False, "form"]], - # "view_mode": "form,tree", - # "view_type": "form", - # "target": "current", "res_id": cpo.id, + "view_type": "form", + "view_mode": "form,tree", + "target": "current", } return action diff --git a/compute_purchase_order/views/computed_purchase_order.xml b/compute_purchase_order/views/computed_purchase_order.xml index f4c9b64..f6d56a1 100644 --- a/compute_purchase_order/views/computed_purchase_order.xml +++ b/compute_purchase_order/views/computed_purchase_order.xml @@ -107,12 +107,7 @@ code - model.generate_cpo() + action = model.generate_cpo() - - - From b2155fdfccac884c0da37fc91fb17b20cf15aa06 Mon Sep 17 00:00:00 2001 From: "robin.keunen" Date: Wed, 29 Apr 2020 19:27:28 +0200 Subject: [PATCH 20/21] [REF] cpo -> pog: rename everything --- compute_purchase_order/models/__init__.py | 4 -- compute_purchase_order/readme/DESCRIPTION.rst | 1 - .../security/ir.model.access.csv | 11 ----- compute_purchase_order/tests/__init__.py | 1 - .../README.rst | 16 +++---- .../__init__.py | 0 .../__manifest__.py | 10 ++--- purchase_order_generator/models/__init__.py | 4 ++ .../models/product_template.py | 0 .../models/purchase_order.py | 6 +-- .../models/purchase_order_generator.py | 32 +++++++------- .../models/purchase_order_generator_line.py | 12 +++--- .../readme/CONTRIBUTORS.rst | 0 .../readme/DESCRIPTION.rst | 1 + .../readme/USAGE.rst | 0 .../security/ir.model.access.csv | 11 +++++ .../static/description/index.html | 16 +++---- purchase_order_generator/tests/__init__.py | 1 + .../tests/test_pog.py | 42 +++++++++---------- .../views/product_template.xml | 0 .../views/purchase_order.xml | 0 .../views/purchase_order_generator.xml | 34 +++++++-------- 22 files changed, 101 insertions(+), 101 deletions(-) delete mode 100644 compute_purchase_order/models/__init__.py delete mode 100644 compute_purchase_order/readme/DESCRIPTION.rst delete mode 100644 compute_purchase_order/security/ir.model.access.csv delete mode 100644 compute_purchase_order/tests/__init__.py rename {compute_purchase_order => purchase_order_generator}/README.rst (75%) rename {compute_purchase_order => purchase_order_generator}/__init__.py (100%) rename {compute_purchase_order => purchase_order_generator}/__manifest__.py (64%) create mode 100644 purchase_order_generator/models/__init__.py rename {compute_purchase_order => purchase_order_generator}/models/product_template.py (100%) rename {compute_purchase_order => purchase_order_generator}/models/purchase_order.py (91%) rename compute_purchase_order/models/computed_purchase_order.py => purchase_order_generator/models/purchase_order_generator.py (84%) rename compute_purchase_order/models/computed_purchase_order_line.py => purchase_order_generator/models/purchase_order_generator_line.py (95%) rename {compute_purchase_order => purchase_order_generator}/readme/CONTRIBUTORS.rst (100%) create mode 100644 purchase_order_generator/readme/DESCRIPTION.rst rename {compute_purchase_order => purchase_order_generator}/readme/USAGE.rst (100%) create mode 100644 purchase_order_generator/security/ir.model.access.csv rename {compute_purchase_order => purchase_order_generator}/static/description/index.html (93%) create mode 100644 purchase_order_generator/tests/__init__.py rename compute_purchase_order/tests/test_cpo.py => purchase_order_generator/tests/test_pog.py (67%) rename {compute_purchase_order => purchase_order_generator}/views/product_template.xml (100%) rename {compute_purchase_order => purchase_order_generator}/views/purchase_order.xml (100%) rename compute_purchase_order/views/computed_purchase_order.xml => purchase_order_generator/views/purchase_order_generator.xml (74%) diff --git a/compute_purchase_order/models/__init__.py b/compute_purchase_order/models/__init__.py deleted file mode 100644 index 6fdf87d..0000000 --- a/compute_purchase_order/models/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -from . import purchase_order -from . import computed_purchase_order -from . import computed_purchase_order_line -from . import product_template diff --git a/compute_purchase_order/readme/DESCRIPTION.rst b/compute_purchase_order/readme/DESCRIPTION.rst deleted file mode 100644 index e9f66f3..0000000 --- a/compute_purchase_order/readme/DESCRIPTION.rst +++ /dev/null @@ -1 +0,0 @@ -Compute purchase order from selected products. diff --git a/compute_purchase_order/security/ir.model.access.csv b/compute_purchase_order/security/ir.model.access.csv deleted file mode 100644 index 982809e..0000000 --- a/compute_purchase_order/security/ir.model.access.csv +++ /dev/null @@ -1,11 +0,0 @@ -id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink - -access_purchase_order,purchase.order,model_computed_purchase_order,purchase.group_purchase_user,1,1,1,1 -access_purchase_order_manager,purchase.order,model_computed_purchase_order,purchase.group_purchase_manager,1,1,1,1 -access_purchase_order_stock_worker,purchase.order,model_computed_purchase_order,stock.group_stock_user,1,0,0,0 -access_purchase_order_invoicing_payments,purchase.order,model_computed_purchase_order,account.group_account_invoice,1,1,0,0 - -access_purchase_order_line,purchase.order.line user,model_computed_purchase_order_line,purchase.group_purchase_user,1,1,1,1 -access_purchase_order_line_manager,purchase.order.line manager,model_computed_purchase_order_line,purchase.group_purchase_manager,1,1,1,1 -access_purchase_order_line_stock_worker,purchase.order.line,model_computed_purchase_order_line,stock.group_stock_user,1,0,0,0 -access_purchase_order_line_invoicing_payments,purchase.order.line,model_computed_purchase_order_line,account.group_account_invoice,1,1,0,0 diff --git a/compute_purchase_order/tests/__init__.py b/compute_purchase_order/tests/__init__.py deleted file mode 100644 index 0246a73..0000000 --- a/compute_purchase_order/tests/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from . import test_cpo diff --git a/compute_purchase_order/README.rst b/purchase_order_generator/README.rst similarity index 75% rename from compute_purchase_order/README.rst rename to purchase_order_generator/README.rst index f8e0c96..5d438e1 100644 --- a/compute_purchase_order/README.rst +++ b/purchase_order_generator/README.rst @@ -1,6 +1,6 @@ -======================= -Computed Purchase Order -======================= +======================== +Purchase Order Generator +======================== .. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !! This file is generated by oca-gen-addon-readme !! @@ -14,12 +14,12 @@ Computed Purchase Order :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :alt: License: AGPL-3 .. |badge3| image:: https://img.shields.io/badge/github-beescoop%2Fobeesdoo-lightgray.png?logo=github - :target: https://github.com/beescoop/obeesdoo/tree/12.0/compute_purchase_order + :target: https://github.com/beescoop/obeesdoo/tree/12.0/purchase_order_generator :alt: beescoop/obeesdoo |badge1| |badge2| |badge3| -Compute purchase order from selected products. +Generate purchase order from a product selection. **Table of contents** @@ -32,7 +32,7 @@ Bug Tracker Bugs are tracked on `GitHub Issues `_. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us smashing it by providing a detailed and welcomed -`feedback `_. +`feedback `_. Do not contact contributors directly about support or help with technical issues. @@ -42,7 +42,7 @@ Credits Authors ~~~~~~~ -* Coop IT Easy +* Coop IT Easy SCRLfs Contributors ~~~~~~~~~~~~ @@ -53,6 +53,6 @@ Contributors Maintainers ~~~~~~~~~~~ -This module is part of the `beescoop/obeesdoo `_ project on GitHub. +This module is part of the `beescoop/obeesdoo `_ project on GitHub. You are welcome to contribute. diff --git a/compute_purchase_order/__init__.py b/purchase_order_generator/__init__.py similarity index 100% rename from compute_purchase_order/__init__.py rename to purchase_order_generator/__init__.py diff --git a/compute_purchase_order/__manifest__.py b/purchase_order_generator/__manifest__.py similarity index 64% rename from compute_purchase_order/__manifest__.py rename to purchase_order_generator/__manifest__.py index 8a9de1e..5587487 100644 --- a/compute_purchase_order/__manifest__.py +++ b/purchase_order_generator/__manifest__.py @@ -3,17 +3,17 @@ # Vincent Van Rossem # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). { - "name": "Computed Purchase Order", + "name": "Purchase Order Generator", "version": "12.0.1.0.0", "category": "Purchase Order", - "summary": "Compute purchase order from selected products", - "author": "Coop IT Easy", - "website": "https://github.com/coopiteasy/procurement-addons", + "summary": "Generate purchase order from a product selection", + "author": "Coop IT Easy SCRLfs", + "website": "https://github.com/beescoop/obeesdoo/", "license": "AGPL-3", "depends": ["purchase", "beesdoo_stock_coverage"], "data": [ "security/ir.model.access.csv", - "views/computed_purchase_order.xml", + "views/purchase_order_generator.xml", "views/purchase_order.xml", ], } diff --git a/purchase_order_generator/models/__init__.py b/purchase_order_generator/models/__init__.py new file mode 100644 index 0000000..78eefbf --- /dev/null +++ b/purchase_order_generator/models/__init__.py @@ -0,0 +1,4 @@ +from . import purchase_order +from . import purchase_order_generator +from . import purchase_order_generator_line +from . import product_template diff --git a/compute_purchase_order/models/product_template.py b/purchase_order_generator/models/product_template.py similarity index 100% rename from compute_purchase_order/models/product_template.py rename to purchase_order_generator/models/product_template.py diff --git a/compute_purchase_order/models/purchase_order.py b/purchase_order_generator/models/purchase_order.py similarity index 91% rename from compute_purchase_order/models/purchase_order.py rename to purchase_order_generator/models/purchase_order.py index c72b2be..b17a488 100644 --- a/compute_purchase_order/models/purchase_order.py +++ b/purchase_order_generator/models/purchase_order.py @@ -9,9 +9,9 @@ class PurchaseOrder(models.Model): _inherit = "purchase.order" original_cpo_id = fields.Many2one( - "computed.purchase.order", - string="Original CPO", - help="CPO used to generate this Purchase Order", + "purchase.order.generator", + string="Original POG", + help="POG used to generate this Purchase Order", ) diff --git a/compute_purchase_order/models/computed_purchase_order.py b/purchase_order_generator/models/purchase_order_generator.py similarity index 84% rename from compute_purchase_order/models/computed_purchase_order.py rename to purchase_order_generator/models/purchase_order_generator.py index 48766db..7c092c8 100644 --- a/compute_purchase_order/models/computed_purchase_order.py +++ b/purchase_order_generator/models/purchase_order_generator.py @@ -7,12 +7,12 @@ from odoo import models, fields, api, _ from odoo.exceptions import ValidationError -class ComputedPurchaseOrder(models.Model): - _description = "Computed Purchase Order" - _name = "computed.purchase.order" +class PurchaseOrderGenerator(models.Model): + _description = "Purchase Order Generator" + _name = "purchase.order.generator" _order = "id desc" - name = fields.Char(string="CPO Reference", default=_("New")) + name = fields.Char(string="POG Reference", default=_("New")) order_date = fields.Datetime( string="Purchase Order Date", default=fields.Datetime.now, @@ -21,7 +21,7 @@ class ComputedPurchaseOrder(models.Model): ) date_planned = fields.Datetime( string="Date Planned", - default=fields.Datetime.now, # default=lambda _: fields.Datetime.now() + default=fields.Datetime.now, ) supplier_id = fields.Many2one( comodel_name="res.partner", @@ -29,13 +29,13 @@ class ComputedPurchaseOrder(models.Model): readonly=True, help="Supplier of the purchase order.", ) - cpo_line_ids = fields.One2many( - comodel_name="computed.purchase.order.line", + pog_line_ids = fields.One2many( + comodel_name="purchase.order.generator.line", inverse_name="cpo_id", string="Order Lines", ) total_amount = fields.Float( - string="Total Amount (w/o VAT)", compute="_compute_cpo_total" + string="Total Amount (w/o VAT)", compute="compute_pog_total" ) generated_purchase_order_ids = fields.One2many( comodel_name="purchase.order", @@ -48,10 +48,10 @@ class ComputedPurchaseOrder(models.Model): ) @api.multi - @api.depends("cpo_line_ids", "cpo_line_ids.purchase_quantity") - def _compute_cpo_total(self): + @api.depends("pog_line_ids", "pog_line_ids.purchase_quantity") + def compute_pog_total(self): for cpo in self: - total_amount = sum(cpol.subtotal for cpol in cpo.cpo_line_ids) + total_amount = sum(cpol.subtotal for cpol in cpo.pog_line_ids) cpo.total_amount = total_amount @api.model @@ -71,11 +71,11 @@ class ComputedPurchaseOrder(models.Model): @api.model def generate_cpo(self): - order_line_obj = self.env["computed.purchase.order.line"] + order_line_obj = self.env["purchase.order.generator.line"] product_ids = self.env.context.get("active_ids", []) supplier = self._get_selected_supplier() - name = "CPO {} {}".format(supplier.name, fields.Date.today()) + name = "POG {} {}".format(supplier.name, fields.Date.today()) cpo = self.create({"name": name, "supplier_id": supplier.id}) for product_id in product_ids: @@ -95,7 +95,7 @@ class ComputedPurchaseOrder(models.Model): ) action = { "type": "ir.actions.act_window", - "res_model": "computed.purchase.order", + "res_model": "purchase.order.generator", "res_id": cpo.id, "view_type": "form", "view_mode": "form,tree", @@ -107,7 +107,7 @@ class ComputedPurchaseOrder(models.Model): def create_purchase_order(self): self.ensure_one() - if sum(self.cpo_line_ids.mapped("purchase_quantity")) == 0: + if sum(self.pog_line_ids.mapped("purchase_quantity")) == 0: raise ValidationError( "You need at least a product to generate " "a Purchase Order" ) @@ -120,7 +120,7 @@ class ComputedPurchaseOrder(models.Model): } ) - for cpo_line in self.cpo_line_ids: + for cpo_line in self.pog_line_ids: if cpo_line.purchase_quantity > 0: pol = self.env["purchase.order.line"].create( { diff --git a/compute_purchase_order/models/computed_purchase_order_line.py b/purchase_order_generator/models/purchase_order_generator_line.py similarity index 95% rename from compute_purchase_order/models/computed_purchase_order_line.py rename to purchase_order_generator/models/purchase_order_generator_line.py index ab97a90..710b7ba 100644 --- a/compute_purchase_order/models/computed_purchase_order_line.py +++ b/purchase_order_generator/models/purchase_order_generator_line.py @@ -10,14 +10,14 @@ from odoo.exceptions import ValidationError _logger = logging.getLogger(__name__) -class ComputedPurchaseOrderLine(models.Model): - _description = "Computed Purchase Order Line" - _name = "computed.purchase.order.line" +class PurchaseOrderGeneratorLine(models.Model): + _description = "Purchase Order Generator Line" + _name = "purchase.order.generator.line" name = fields.Char(string="Product Name", compute="_compute_name") cpo_id = fields.Many2one( - comodel_name="computed.purchase.order", - string="Computed Purchase Order", + comodel_name="purchase.order.generator", + string="Purchase Order Generator", ) product_template_id = fields.Many2one( comodel_name="product.template", @@ -146,7 +146,7 @@ class ComputedPurchaseOrderLine(models.Model): if len(si) == 0: raise ValidationError( - _("CPO supplier does not sell product {name}").format( + _("POG supplier does not sell product {name}").format( name=cpol.product_template_id.name ) ) diff --git a/compute_purchase_order/readme/CONTRIBUTORS.rst b/purchase_order_generator/readme/CONTRIBUTORS.rst similarity index 100% rename from compute_purchase_order/readme/CONTRIBUTORS.rst rename to purchase_order_generator/readme/CONTRIBUTORS.rst diff --git a/purchase_order_generator/readme/DESCRIPTION.rst b/purchase_order_generator/readme/DESCRIPTION.rst new file mode 100644 index 0000000..ea924d5 --- /dev/null +++ b/purchase_order_generator/readme/DESCRIPTION.rst @@ -0,0 +1 @@ +Generate purchase order from a product selection. diff --git a/compute_purchase_order/readme/USAGE.rst b/purchase_order_generator/readme/USAGE.rst similarity index 100% rename from compute_purchase_order/readme/USAGE.rst rename to purchase_order_generator/readme/USAGE.rst diff --git a/purchase_order_generator/security/ir.model.access.csv b/purchase_order_generator/security/ir.model.access.csv new file mode 100644 index 0000000..dbb4063 --- /dev/null +++ b/purchase_order_generator/security/ir.model.access.csv @@ -0,0 +1,11 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink + +access_purchase_order,purchase.order,model_purchase_order_generator,purchase.group_purchase_user,1,1,1,1 +access_purchase_order_manager,purchase.order,model_purchase_order_generator,purchase.group_purchase_manager,1,1,1,1 +access_purchase_order_stock_worker,purchase.order,model_purchase_order_generator,stock.group_stock_user,1,0,0,0 +access_purchase_order_invoicing_payments,purchase.order,model_purchase_order_generator,account.group_account_invoice,1,1,0,0 + +access_purchase_order_line,purchase.order.line user,model_purchase_order_generator_line,purchase.group_purchase_user,1,1,1,1 +access_purchase_order_line_manager,purchase.order.line manager,model_purchase_order_generator_line,purchase.group_purchase_manager,1,1,1,1 +access_purchase_order_line_stock_worker,purchase.order.line,model_purchase_order_generator_line,stock.group_stock_user,1,0,0,0 +access_purchase_order_line_invoicing_payments,purchase.order.line,model_purchase_order_generator_line,account.group_account_invoice,1,1,0,0 diff --git a/compute_purchase_order/static/description/index.html b/purchase_order_generator/static/description/index.html similarity index 93% rename from compute_purchase_order/static/description/index.html rename to purchase_order_generator/static/description/index.html index 699639d..bf49414 100644 --- a/compute_purchase_order/static/description/index.html +++ b/purchase_order_generator/static/description/index.html @@ -4,7 +4,7 @@ -Computed Purchase Order +Purchase Order Generator -
-

Computed Purchase Order

+
+

Purchase Order Generator

-

Beta License: AGPL-3 beescoop/obeesdoo

-

Compute purchase order from selected products.

+

Beta License: AGPL-3 beescoop/obeesdoo

+

Generate purchase order from a product selection.

Table of contents

    @@ -386,7 +386,7 @@ ul.auto-toc {

    Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us smashing it by providing a detailed and welcomed -feedback.

    +feedback.

    Do not contact contributors directly about support or help with technical issues.

@@ -394,7 +394,7 @@ If you spotted it first, help us smashing it by providing a detailed and welcome

Authors

    -
  • Coop IT Easy
  • +
  • Coop IT Easy SCRLfs
@@ -406,7 +406,7 @@ If you spotted it first, help us smashing it by providing a detailed and welcome

Maintainers

-

This module is part of the beescoop/obeesdoo project on GitHub.

+

This module is part of the beescoop/obeesdoo project on GitHub.

You are welcome to contribute.

diff --git a/purchase_order_generator/tests/__init__.py b/purchase_order_generator/tests/__init__.py new file mode 100644 index 0000000..ef21616 --- /dev/null +++ b/purchase_order_generator/tests/__init__.py @@ -0,0 +1 @@ +from . import test_pog diff --git a/compute_purchase_order/tests/test_cpo.py b/purchase_order_generator/tests/test_pog.py similarity index 67% rename from compute_purchase_order/tests/test_cpo.py rename to purchase_order_generator/tests/test_pog.py index c50830b..4a5f9d4 100644 --- a/compute_purchase_order/tests/test_cpo.py +++ b/purchase_order_generator/tests/test_pog.py @@ -30,40 +30,40 @@ class TestCPO(TransactionCase): ] ) - cpo_obj = self.env["computed.purchase.order"] - cpo_action = cpo_obj.with_context( + pog_obj = self.env["purchase.order.generator"] + pog_action = pog_obj.with_context( active_ids=[self.ptemplate1.id] ).generate_cpo() - cpo = cpo_obj.browse(cpo_action["res_id"]) - cpol = cpo.cpo_line_ids # expect one line + pog = pog_obj.browse(pog_action["res_id"]) + pogl = pog.pog_line_ids # expect one line - self.assertEquals(cpo.supplier_id, self.supplier) - self.assertEquals(cpol.product_template_id, self.ptemplate1) - self.assertEquals(cpol.product_price, supplierinfo.price) - self.assertEquals(cpol.purchase_quantity, supplierinfo.min_qty) + self.assertEquals(pog.supplier_id, self.supplier) + self.assertEquals(pogl.product_template_id, self.ptemplate1) + self.assertEquals(pogl.product_price, supplierinfo.price) + self.assertEquals(pogl.purchase_quantity, supplierinfo.min_qty) # testing triggers expected_subtotal = supplierinfo.price * supplierinfo.min_qty - self.assertEquals(cpol.subtotal, expected_subtotal) + self.assertEquals(pogl.subtotal, expected_subtotal) - cpol.purchase_quantity = 4 + pogl.purchase_quantity = 4 expected_subtotal = supplierinfo.price * 4 - self.assertEquals(cpol.subtotal, expected_subtotal) + self.assertEquals(pogl.subtotal, expected_subtotal) - cpo_form = Form(cpo) - with cpo_form.cpo_line_ids.edit(index=0) as line_form: + pog_form = Form(pog) + with pog_form.pog_line_ids.edit(index=0) as line_form: line_form.product_template_id = self.ptemplate2 self.assertEquals(line_form.product_template_id, self.ptemplate2) - cpo = cpo_form.save() - cpol = cpo.cpo_line_ids + pog = pog_form.save() + pogl = pog.pog_line_ids expected_subtotal = supplierinfo2.price * supplierinfo2.min_qty - self.assertEquals(cpol.product_price, supplierinfo2.price) - self.assertEquals(cpol.purchase_quantity, supplierinfo2.min_qty) - self.assertEquals(cpol.subtotal, expected_subtotal) + self.assertEquals(pogl.product_price, supplierinfo2.price) + self.assertEquals(pogl.purchase_quantity, supplierinfo2.min_qty) + self.assertEquals(pogl.subtotal, expected_subtotal) def test_generate_po(self): - cpo_obj = self.env["computed.purchase.order"] + cpo_obj = self.env["purchase.order.generator"] cpo_action = cpo_obj.with_context( active_ids=[self.ptemplate1.id, self.ptemplate2.id] ).generate_cpo() @@ -72,9 +72,9 @@ class TestCPO(TransactionCase): po = self.env["purchase.order"].browse(po_action["res_id"]) self.assertEquals(cpo.supplier_id, po.partner_id) - self.assertEquals(len(cpo.cpo_line_ids), len(po.order_line)) + self.assertEquals(len(cpo.pog_line_ids), len(po.order_line)) lines = zip( - cpo.cpo_line_ids.sorted(lambda l: l.product_template_id), + cpo.pog_line_ids.sorted(lambda l: l.product_template_id), po.order_line.sorted(lambda l: l.product_id.product_tmpl_id), ) for cpol, pol in lines: diff --git a/compute_purchase_order/views/product_template.xml b/purchase_order_generator/views/product_template.xml similarity index 100% rename from compute_purchase_order/views/product_template.xml rename to purchase_order_generator/views/product_template.xml diff --git a/compute_purchase_order/views/purchase_order.xml b/purchase_order_generator/views/purchase_order.xml similarity index 100% rename from compute_purchase_order/views/purchase_order.xml rename to purchase_order_generator/views/purchase_order.xml diff --git a/compute_purchase_order/views/computed_purchase_order.xml b/purchase_order_generator/views/purchase_order_generator.xml similarity index 74% rename from compute_purchase_order/views/computed_purchase_order.xml rename to purchase_order_generator/views/purchase_order_generator.xml index f6d56a1..ad5c78d 100644 --- a/compute_purchase_order/views/computed_purchase_order.xml +++ b/purchase_order_generator/views/purchase_order_generator.xml @@ -3,9 +3,9 @@ - - computed.purchase.order.tree - computed.purchase.order + + purchase.order.generator.tree + purchase.order.generator @@ -18,9 +18,9 @@ - - computed.purchase.order.form - computed.purchase.order + + purchase.order.generator.form + purchase.order.generator
@@ -55,10 +55,10 @@ - + - + @@ -79,8 +79,8 @@ - - computed.purchase.order + + purchase.order.generator @@ -90,20 +90,20 @@ - - Computed Purchase Orders - computed.purchase.order + + Purchase Order Generators + purchase.order.generator - + action="action_purchase_order_generator"/> - Compute Purchase Order - + Generate Purchase Order + code From ec6ebd245f1d934a172f4ccc85829ac1b4336928 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Taymans?= Date: Wed, 6 May 2020 22:16:31 +0200 Subject: [PATCH 21/21] [REF] purchase_order_generator: remove comment --- purchase_order_generator/views/product_template.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/purchase_order_generator/views/product_template.xml b/purchase_order_generator/views/product_template.xml index a600743..b326eec 100644 --- a/purchase_order_generator/views/product_template.xml +++ b/purchase_order_generator/views/product_template.xml @@ -5,7 +5,6 @@ product.template.tree product.template -