Skip to content
This repository has been archived by the owner on Apr 28, 2022. It is now read-only.

10.0 add product configurator to purchase order #42

Open
wants to merge 8 commits into
base: 10.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions product_configurator/models/product.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ class ProductTemplate(models.Model):

config_ok = fields.Boolean(string='Can be Configured')

reuse_variant = fields.Boolean(string='Reuse variants if exists')

config_line_ids = fields.One2many(
comodel_name='product.config.line',
inverse_name='product_tmpl_id',
Expand Down
3 changes: 3 additions & 0 deletions product_configurator/views/product_view.xml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@
<!-- TODO: Apply domains so only values from template are available -->
<xpath expr="//notebook/page[@name='variants']" position="after">
<page string="Configurator" attrs="{'invisible': [('config_ok','=',False)]}">
<separator colspan="4" string="Configuration Options"/>
<field name="reuse_variant"/>
<label for="reuse_variant"/>
<separator colspan="4" string="Configuration Restrictions"/>
<field name="config_line_ids"
attrs="{'readonly': [('attribute_line_ids','=',[])]}"
Expand Down
13 changes: 13 additions & 0 deletions product_configurator_purchase/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#Odoo Product Configurator Purchase

This module is an extension to product configurator to add widjet to purchase and add possibility to choose a product variant instead of always create a new one.

Features
========

- Add configure button to Purchase order.

Usage
=====

To complete
3 changes: 3 additions & 0 deletions product_configurator_purchase/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-

from . import models
29 changes: 29 additions & 0 deletions product_configurator_purchase/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# -*- coding: utf-8 -*-

{
'name': 'Product Configurator Purchase',
'version': '10.0',
'category': 'Generic Modules/Base',
'summary': 'product configuration interface for purchase',
'description': """
Purchase Product Configurator
""",
'author': 'Microcom',
'license': 'AGPL-3',
'website': 'http://www.microcom.ca/',
'depends': [
'purchase',
'product_configurator',
'product_configurator_wizard',
],
"data": [
'views/purchase_views.xml',
],
'demo': [
],
'images': [
],
'test': [],
'installable': True,
'auto_install': False,
}
3 changes: 3 additions & 0 deletions product_configurator_purchase/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-

from . import purchase
40 changes: 40 additions & 0 deletions product_configurator_purchase/models/product_configurator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# -*- coding: utf-8 -*-

from odoo import models, fields, api


class ProductConfigurator(models.Model):
_inherit = 'product.configurator'

purchase_order_line_id = fields.Many2one(
comodel_name='purchase.order.line',
readonly=True,
)

@api.multi
def action_config_done(self):
"""This fuction are copied from product_configurator_wizard.ProductConfigurator
We only add one "if" required for purchase_order
Parse values and execute final code before closing the wizard"""
custom_vals = {
l.attribute_id.id:
l.value or l.attachment_ids for l in self.custom_value_ids
}

if self.product_id:
remove_cv_links = map(lambda cv: (2, cv), self.product_id.value_custom_ids.ids)
new_cv_links = self.product_id.product_tmpl_id.encode_custom_values(custom_vals)
self.product_id.write({
'attribute_value_ids': [(6, 0, self.value_ids.ids)],
'value_custom_ids': remove_cv_links + new_cv_links,
})
if self.order_line_id:
self.order_line_id.write(self._extra_line_values(self.order_line_id.order_id,
self.product_id,
new=False))
if self.purchase_order_line_id:
self.purchase_order_line_id.write(self._extra_line_values(self.purchase_order_line_id.order_id,
self.product_id,
new=False))
self.unlink()
return
50 changes: 50 additions & 0 deletions product_configurator_purchase/models/purchase.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# -*- coding: utf-8 -*-

from odoo import models, fields, api


class PurchaseOrderLine(models.Model):
_inherit = 'purchase.order.line'

custom_value_ids = fields.One2many(
comodel_name='product.attribute.value.custom',
inverse_name='product_id',
related="product_id.value_custom_ids",
string="Custom Values"
)

product_id = fields.Many2one(domain=[('config_ok', '=', False)])

config_ok = fields.Boolean(
related='product_id.config_ok',
string="Configurable"
)

@api.multi
def reconfigure_product(self):
""" Creates and launches a product configurator wizard with a linked
template and variant in order to re-configure a existing product. It is
esetially a shortcut to pre-fill configuration data of a variant"""

cfg_steps = self.product_id.product_tmpl_id.config_step_line_ids
active_step = str(cfg_steps[0].id) if cfg_steps else 'configure'

wizard_obj = self.env['product.configurator']
wizard = wizard_obj.create({
'product_id': self.product_id.id,
'state': active_step,
'purchase_order_line_id': self.id,
})

return {
'type': 'ir.actions.act_window',
'res_model': 'product.configurator',
'name': "Configure Product",
'view_mode': 'form',
'context': dict(
self.env.context,
wizard_id=wizard.id,
),
'target': 'new',
'res_id': wizard.id,
}
16 changes: 16 additions & 0 deletions product_configurator_purchase/static/description/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<section class="oe_container">
<div class="oe_row oe_spaced">
<div class="oe_span12">
<h2 class="oe_slogan">Product Configurator Purchase</h2>
<h3 class="oe_slogan">Generate products on-demand, easy and error-free</h3>
</div>
</div>
</section>

<section class="oe_container mt64">
<div class="oe_row oe_spaced">
<h2 class="oe_slogan">Configure products directly in your purchase orders</h2>
<h3 class="oe_slogan">This module is an extention to product configurator and Product configurator Wizard</h3>
<h3 class="oe_slogan">It allow to configure product from purchase orders in addition to Sale orders</h3>
</div>
</section>
53 changes: 53 additions & 0 deletions product_configurator_purchase/views/purchase_views.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>

<!-- Add Configure Product button on sale order form view -->

<record id="action_wizard_product_purchase_configurator" model="ir.actions.act_window">
<field name="name">Configure Product</field>
<field name="res_model">product.configurator</field>
<field name="view_mode">form</field>
<field name="view_id" ref="product_configurator_wizard.product_configurator_form"/>
<field name="target">new</field>
</record>

<record id="purchase_order_form_config" model="ir.ui.view">
<field name="name">purchase.order.form.config</field>
<field name="model">purchase.order</field>
<field name="inherit_id" ref="purchase.purchase_order_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='order_line']" position="before">
<button name="%(action_wizard_product_purchase_configurator)s"
states="draft,sent"
class="oe_highlight"
type="action"
style="margin-top: 15px;margin-bottom: 10px;"
string="Configure Product"
groups="product_configurator.group_product_configurator"/>
</xpath>

<xpath expr="//field[@name='order_line']/form//field[@name='product_id']" position="after">
<field name="config_ok" invisible="1"/>
</xpath>

<xpath expr="//field[@name='order_line']/tree//field[@name='product_id']" position="after">
<field name="config_ok" invisible="1"/>
<button name="reconfigure_product" groups="product_configurator.group_product_configurator"
help="Reconfigure"
icon="fa-cogs"
type="object"
attrs="{
'invisible': [
'|',
('config_ok','=',False),
('state','not in',['draft','sent'])
]}"/>
</xpath>

<xpath expr="//label[@for='date_planned']/../div/field[@name='date_planned']" position="attributes">
<attribute name="required">0</attribute>
</xpath>
</field>
</record>

</odoo>
59 changes: 41 additions & 18 deletions product_configurator_wizard/wizard/product_configurator.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@

from lxml import etree

from datetime import datetime
from odoo.osv import orm
from odoo import models, fields, api, _
from odoo.exceptions import Warning, ValidationError
from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT


class FreeSelection(fields.Selection):
Expand Down Expand Up @@ -792,10 +794,8 @@ def _extra_line_values(self, so, product, new=True):
created or edited lines."""
vals = {}
if new:
vals.update({
'name': product.display_name,
'product_uom': product.uom_id.id,
})
vals.update({'name': product.display_name})
vals.update({'product_uom': product.uom_id.id})
return vals

@api.multi
Expand Down Expand Up @@ -829,23 +829,46 @@ def action_config_done(self):
# In the meantime, at least make sure that a validation
# error legitimately raised in a nested routine
# is passed through.
try:
variant = self.product_tmpl_id.create_variant(
self.value_ids.ids, custom_vals)
except ValidationError:
raise
except:
raise ValidationError(
_('Invalid configuration! Please check all '
'required steps and fields.')
)
domain = [['product_tmpl_id', '=', self.product_tmpl_id.id]]

template_products = self.env['product.product'].search(domain)
product_found = False

so = self.env['sale.order'].browse(self.env.context.get('active_id'))
if self.product_tmpl_id.reuse_variant:
for product in template_products:
if product.attribute_value_ids == self.value_ids:
product_found = product
break

if product_found:
variant = product_found
price = product_found.standard_price
uom = product_found.uom_id.id
else:
try:
variant = self.product_tmpl_id.create_variant(
self.value_ids.ids, custom_vals)
price = self.product_tmpl_id.standard_price
uom = self.product_tmpl_id.uom_id.id
except ValidationError:
raise
except:
raise ValidationError(
_('Invalid configuration! Please check all '
'required steps and fields.')
)

if self.env.context.get('active_model') == 'purchase.order':
order = self.env['purchase.order'].browse(self.env.context.get('active_id'))
line_vals = {'product_id': variant.id, 'date_planned': datetime.today().strftime(DEFAULT_SERVER_DATETIME_FORMAT),
'product_uom': uom, 'price_unit': price, 'product_qty': 1}
else:
order = self.env['sale.order'].browse(self.env.context.get('active_id'))
line_vals = {'product_id': variant.id}

line_vals = {'product_id': variant.id}
line_vals.update(self._extra_line_values(so, variant, new=True))
line_vals.update(self._extra_line_values(order, variant, new=True))

so.write({
order.write({
'order_line': [(0, 0, line_vals)]
})

Expand Down