[MERGE] master

This commit is contained in:
qdp-odoo 2014-05-30 16:52:03 +02:00
commit 0fa755bc89
340 changed files with 6643 additions and 14194 deletions

2
.gitignore vendored
View File

@ -25,4 +25,4 @@ install/win32/meta.py
/lib/
/man/
/share/
/src/
/src/

View File

@ -1,73 +1,52 @@
About Odoo
==========
Odoo
----
Odoo is a suite of open source Business apps. More info at http://www.odoo.com
Odoo is a suite of web based open source business apps. More info at http://www.odoo.com
Installation
============
[Setup/migration guide for employees](https://github.com/odoo/odoo/blob/master/doc/git.rst)
The easiest way to play with it is the <a href="https://www.odoo.com/page/start">Odoo free trial</a>, email registration is NOT required, use the "skip this step" link on the registration page to skip it.
Migration from bazaar
=====================
Getting started with Odoo developement
--------------------------------------
If you have existing bazaar branches and want to move them to a git repository,
there are several options:
If you are a developer type the following command at your terminal [1]:
* download http://nightly.openerp.com/move-branch.zip and run it with
`python move-branch.zip -h` (for the help). It should be able to convert
simple-enough branches for you (even if they have merge commits &al)
* Extract the branch contents as patches and use `git apply` or `git am` to
rebuild a branch from them
* Replay the branch by hand
wget -O- https://raw.githubusercontent.com/odoo/odoo/master/odoo.py | python
Then follow <a href="https://doc.openerp.com/trunk/server/howto/howto_website/">the developer tutorial</a>
[1] You may want to check the content of the <a href="https://raw.githubusercontent.com/odoo/odoo/master/odoo.py">odoo.py file</a> before executing it.
System Requirements
-------------------
Packages, tarballs and installers
---------------------------------
The dependencies are listed in setup.py
* Debian packages
Add this apt repository to your /etc/apt/sources.list file
deb http://nightly.openerp.com/8.0/deb/ ./
Then type:
$ sudo apt-get update
$ sudo apt-get install odoo
* <a href="http://nightly.openerp.com/">Source tarballs</a>
* <a href="http://nightly.openerp.com/">Windows installer</a>
* <a href="http://nightly.openerp.com/">RPM package</a>
Debian/Ubuntu
-------------
For Odoo employees
------------------
Add the apt repository
To add the odoo-dev remote use this command:
deb http://nightly.openerp.com/7.0/deb/ ./
$ ./odoo.py setup_git_dev
in your source.list and type:
To fetch odoo merge pull requests refs use this command:
$ sudo apt-get update
$ sudo apt-get install openerp
Or download the deb file and type:
$ sudo dpkg -i <openerp-deb-filename>
$ sudo apt-get install -f
RedHat, Fedora, CentOS
----------------------
Install the required dependencies:
$ yum install python
$ easy_install pip
$ pip install .....
Install the openerp rpm
$ rpm -i openerp-VERSION.rpm
Windows
-------
Check the notes in setup.py
Setting up your database
------------------------
Point your browser to http://localhost:8069/ and click "Manage Databases", the
default master password is "admin".
$ ./odoo.py setup_git_review

View File

@ -60,7 +60,7 @@
</group>
<group>
<field domain="[('company_id', '=', parent.company_id), ('journal_id', '=', parent.journal_id), ('type', '&lt;&gt;', 'view')]" name="account_id" on_change="onchange_account_id(product_id, parent.partner_id, parent.type, parent.fiscal_position,account_id)" groups="account.group_account_user"/>
<field name="invoice_line_tax_id" context="{'type':parent.type}" domain="[('parent_id','=',False),('company_id', '=', parent.company_id)]" widget="many2many_tags"/>
<field name="invoice_line_tax_id" context="{'type':parent.get('type')}" domain="[('parent_id','=',False),('company_id', '=', parent.company_id)]" widget="many2many_tags"/>
<field domain="[('type','&lt;&gt;','view'), ('company_id', '=', parent.company_id)]" name="account_analytic_id" groups="analytic.group_analytic_accounting"/>
<field name="company_id" groups="base.group_multi_company" readonly="1"/>
</group>

View File

@ -1,57 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="product_normal_form_view" model="ir.ui.view">
<field name="name">product.normal.form.inherit</field>
<field name="model">product.product</field>
<record id="product_template_form_view" model="ir.ui.view">
<field name="name">product.template.form.inherit</field>
<field name="model">product.template</field>
<field name="priority">5</field>
<field name="inherit_id" ref="product.product_normal_form_view"/>
<field name="inherit_id" ref="product.product_template_form_view"/>
<field name="arch" type="xml">
<notebook position="inside">
<page string="Sales" position="after">
<page string="Accounting" groups="account.group_account_invoice">
<group>
<label for="categ_id" string="Internal Category"/>
<div><field name="categ_id" colspan="3" nolabel="1"/></div>
</group>
<group name="properties">
<group>
<field name="property_account_income" domain="[('type','=','other')]" groups="account.group_account_user"
attrs="{'readonly': [('is_only_child', '=', False)]}"/>
attrs="{'readonly': [('is_product_variant', '=', True)]}"/>
<field name="taxes_id" colspan="2" widget="many2many_tags"
attrs="{'readonly':[ '|', ('sale_ok','=',0), ('is_only_child', '=', False)]}"/>
attrs="{'readonly':[ '|', ('sale_ok','=',0), ('is_product_variant', '=', True)]}"/>
</group>
<group>
<field name="property_account_expense" domain="[('type','=','other')]" groups="account.group_account_user"
attrs="{'readonly': [('is_only_child', '=', False)]}"/>
attrs="{'readonly': [('is_product_variant', '=', True)]}"/>
<field name="supplier_taxes_id" colspan="2" widget="many2many_tags"
attrs="{'readonly': [('is_only_child', '=', False)]}"/>
attrs="{'readonly': [('is_product_variant', '=', True)]}"/>
</group>
</group>
</page>
</notebook>
</page>
</field>
</record>
<record id="product_template_form_view" model="ir.ui.view">
<field name="name">product.template.product.form.inherit</field>
<record id="product_template_search_view" model="ir.ui.view">
<field name="name">product.template.search</field>
<field name="model">product.template</field>
<field name="inherit_id" ref="product.product_template_form_view"/>
<field name="mode">primary</field>
<field name="inherit_id" ref="product.product_template_search_view"/>
<field name="arch" type="xml">
<notebook position="inside">
<page string="Accounting">
<group name="properties">
<group>
<field name="property_account_income" domain="[('type','&lt;&gt;','view'),('type','&lt;&gt;','consolidation')]"/>
<field name="taxes_id" colspan="2" widget="many2many_tags"/>
</group>
<group>
<field name="property_account_expense" domain="[('type','&lt;&gt;','view'),('type','&lt;&gt;','consolidation')]"/>
<field name="supplier_taxes_id" colspan="2" widget="many2many_tags"/>
</group>
</group>
</page>
</notebook>
</field>
<field name="product_variant_ids" position="after">
<field name="categ_id"/>
</field>
<xpath expr="//group[@string='Group by...']" position="inside">
<filter string='Category' icon="terp-stock_symbol-selection" domain="[]" context="{'group_by' : 'categ_id'}"/>
</xpath>
</field>
</record>
<record id="view_category_property_form" model="ir.ui.view">
<field name="name">product.category.property.form.inherit</field>
<field name="model">product.category</field>

View File

@ -161,7 +161,7 @@ class aged_trial_report(report_sxw.rml_parse, common_report_header):
dates_query += ' < %s)'
args_list += (form[str(i)]['stop'],)
args_list += (self.date_from,)
self.cr.execute('''SELECT l.partner_id, SUM(l.debit-l.credit)
self.cr.execute('''SELECT l.partner_id, SUM(l.debit-l.credit), l.reconcile_partial_id
FROM account_move_line AS l, account_account, account_move am
WHERE (l.account_id = account_account.id) AND (l.move_id=am.id)
AND (am.state IN %s)
@ -173,12 +173,24 @@ class aged_trial_report(report_sxw.rml_parse, common_report_header):
AND account_account.active
AND ''' + dates_query + '''
AND (l.date <= %s)
GROUP BY l.partner_id''', args_list)
t = self.cr.fetchall()
d = {}
for i in t:
d[i[0]] = i[1]
history.append(d)
GROUP BY l.partner_id, l.reconcile_partial_id''', args_list)
partners_partial = self.cr.fetchall()
partners_amount = dict((i[0],0) for i in partners_partial)
for partner_info in partners_partial:
if partner_info[2]:
# in case of partial reconciliation, we want to keep the left amount in the oldest period
self.cr.execute('''SELECT MIN(COALESCE(date_maturity,date)) FROM account_move_line WHERE reconcile_partial_id = %s''', (partner_info[2],))
date = self.cr.fetchall()
if date and args_list[-3] <= date[0][0] <= args_list[-2]:
# partial reconcilation
self.cr.execute('''SELECT SUM(l.debit-l.credit)
FROM account_move_line AS l
WHERE l.reconcile_partial_id = %s''', (partner_info[2],))
unreconciled_amount = self.cr.fetchall()
partners_amount[partner_info[0]] += unreconciled_amount[0][0]
else:
partners_amount[partner_info[0]] += partner_info[1]
history.append(partners_amount)
for partner in partners:
values = {}

View File

@ -5,7 +5,7 @@
<t t-call="report.external_layout">
<div class="page">
<div class="row">
<div class="col-xs-4 col-xs-offset-8">
<div class="col-xs-5 col-xs-offset-7">
<address t-field="o.partner_id"
t-field-options='{"widget": "contact", "fields": ["address", "name"], "no_marker": true}' />
<span t-field="o.partner_id.vat"/>

View File

@ -5,7 +5,7 @@
<t t-call="report.external_layout">
<div class="page">
<div class="row">
<div class="col-xs-4 col-xs-offset-6">
<div class="col-xs-5 col-xs-offset-7">
<span t-field="o.name"/><br/>
<span t-raw="addresses[o.id].replace('\n\n', '\n').replace('\n', '&lt;br&gt;')"/>
<span t-field="o.vat"/>

View File

@ -69,7 +69,7 @@ class account_analytic_invoice_line(osv.osv):
if partner_id:
part = self.pool.get('res.partner').browse(cr, uid, partner_id, context=local_context)
if part.lang:
context.update({'lang': part.lang})
local_context.update({'lang': part.lang})
result = {}
res = self.pool.get('product.product').browse(cr, uid, product, context=local_context)
@ -79,7 +79,12 @@ class account_analytic_invoice_line(osv.osv):
price = res.price
else:
price = res.list_price
result.update({'name': name or res.description or False,'uom_id': uom_id or res.uom_id.id or False, 'price_unit': price})
if not name:
name = self.pool.get('product.product').name_get(cr, uid, [res.id], context=local_context)[0][1]
if res.description_sale:
name += '\n'+res.description_sale
result.update({'name': name or False,'uom_id': uom_id or res.uom_id.id or False, 'price_unit': price})
res_final = {'value':result}
if result['uom_id'] != res.uom_id.id:

View File

@ -169,7 +169,7 @@
<div attrs="{'invisible': [('recurring_invoices','=',False)]}">
<field name="recurring_invoice_line_ids">
<tree string="Account Analytic Lines" editable="bottom">
<field name="product_id" on_change="product_id_change(product_id, uom_id, quantity, name, parent.partner_id, price_unit, parent.pricelist_id, parent.company_id)"/>
<field name="product_id" on_change="product_id_change(product_id, uom_id, quantity, False, parent.partner_id, False, parent.pricelist_id, parent.company_id)"/>
<field name="name"/>
<field name="quantity"/>
<field name="uom_id"/>

View File

@ -1,29 +1,18 @@
<?xml version="1.0"?>
<openerp>
<data>
<record id="product_normal_form_view" model="ir.ui.view">
<field name="name">product.normal.form.inherit.stock</field>
<field name="model">product.product</field>
<field name="inherit_id" ref="account.product_normal_form_view"/>
<field name="arch" type="xml">
<field name="property_account_expense" position="after">
<label string="" colspan="2"/>
<field name="property_account_creditor_price_difference" domain="[('type','&lt;&gt;','view'),('type','&lt;&gt;','consolidation')]" attrs="{'readonly':[('purchase_ok','=',0)]}" />
<newline/>
</field>
</field>
</record>
<record id="product_template_form_view" model="ir.ui.view">
<field name="name">product.template.product.form.inherit</field>
<field name="name">product.normal.form.inherit.stock</field>
<field name="model">product.template</field>
<field name="inherit_id" ref="account.product_template_form_view"/>
<field name="arch" type="xml">
<field name="property_account_expense" position="after">
<label string="" colspan="2"/>
<field name="purchase_ok" invisible="1"/>
<field name="property_account_creditor_price_difference" domain="[('type','&lt;&gt;','view'),('type','&lt;&gt;','consolidation')]" attrs="{'readonly':[('purchase_ok','=',0)]}" />
<newline/>
</field>
</field>
</field>
</record>
<record id="view_category_property_form" model="ir.ui.view">

View File

@ -22,6 +22,7 @@
import datetime
from openerp.osv import fields, osv
from openerp.tools import ustr
from openerp.tools.translate import _
import openerp.addons.decimal_precision as dp
@ -114,7 +115,7 @@ class crossovered_budget_lines(osv.osv):
for line in self.browse(cr, uid, ids, context=context):
acc_ids = [x.id for x in line.general_budget_id.account_ids]
if not acc_ids:
raise osv.except_osv(_('Error!'),_("The Budget '%s' has no accounts!") % str(line.general_budget_id.name))
raise osv.except_osv(_('Error!'),_("The Budget '%s' has no accounts!") % ustr(line.general_budget_id.name))
date_to = line.date_to
date_from = line.date_from
if context.has_key('wizard_date_from'):

View File

@ -7,7 +7,7 @@
<t t-call="report.external_layout">
<div class="page">
<div class="row">
<div class="col-xs-4 col-xs-offset-6">
<div class="col-xs-5 col-xs-offset-7">
<div t-field="o.partner_id"
t-field-options='{"widget": "contact", "fields": ["address", "name", "phone", "fax"], "no_marker": true}'/>
<span t-field="o.partner_id.vat"/>

View File

@ -3,7 +3,7 @@
========================
******
saas-5
saas-4
******
- Library update: ``mygengo`` (https://pypi.python.org/pypi/mygengo/1.3.3) was outdated and has been replaced by ``gengo`` (https://pypi.python.org/pypi/gengo).
- Library update: ``mygengo`` (https://pypi.python.org/pypi/mygengo/1.3.3) was outdated and has been replaced by ``gengo`` (https://pypi.python.org/pypi/gengo).

View File

@ -65,7 +65,7 @@ class res_partner(osv.osv):
'date_review_next' : fields.date('Next Partner Review'),
# customer implementation
'assigned_partner_id': fields.many2one(
'res.partner', 'Implementedy by',
'res.partner', 'Implemented by',
),
'implemented_partner_ids': fields.one2many(
'res.partner', 'assigned_partner_id',

View File

@ -3,6 +3,7 @@ access_ crm_lead_report_assign,crm.lead.report.assign,model_crm_lead_report_assi
access_ crm_lead_report_assign_all,crm.lead.report.assign.all,model_crm_lead_report_assign,base.group_user,1,0,0,0
access_crm_partner_report,crm.partner.report.assign.all,model_crm_partner_report_assign,base.group_sale_salesman,1,0,0,0
access_res_partner_grade,res.partner.grade,model_res_partner_grade,base.group_sale_salesman,1,1,1,0
access_res_partner_grade_public,res.partner.grade,model_res_partner_grade,base.group_public,1,0,0,0
access_res_partner_grade_manager,res.partner.grade.manager,model_res_partner_grade,base.group_sale_manager,1,1,1,1
"access_partner_activation_manager","res.partner.activation.manager","model_res_partner_activation","base.group_partner_manager",1,1,1,1
partner_access_crm_lead,crm.lead,model_crm_lead,base.group_portal,1,1,0,0
partner_access_crm_lead,crm.lead,model_crm_lead,base.group_portal,1,1,0,0

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
3 access_ crm_lead_report_assign_all crm.lead.report.assign.all model_crm_lead_report_assign base.group_user 1 0 0 0
4 access_crm_partner_report crm.partner.report.assign.all model_crm_partner_report_assign base.group_sale_salesman 1 0 0 0
5 access_res_partner_grade res.partner.grade model_res_partner_grade base.group_sale_salesman 1 1 1 0
6 access_res_partner_grade_public res.partner.grade model_res_partner_grade base.group_public 1 0 0 0
7 access_res_partner_grade_manager res.partner.grade.manager model_res_partner_grade base.group_sale_manager 1 1 1 1
8 access_partner_activation_manager res.partner.activation.manager model_res_partner_activation base.group_partner_manager 1 1 1 1
9 partner_access_crm_lead crm.lead model_crm_lead base.group_portal 1 1 0 0

View File

@ -192,15 +192,16 @@ class delivery_grid(osv.osv):
weight = 0
volume = 0
quantity = 0
product_uom_obj = self.pool.get('product.uom')
for line in order.order_line:
if not line.product_id or line.is_delivery:
continue
weight += (line.product_id.weight or 0.0) * line.product_uom_qty
volume += (line.product_id.volume or 0.0) * line.product_uom_qty
quantity += line.product_uom_qty
q = product_uom_obj._compute_qty(cr, uid, line.product_uom.id, line.product_uos_qty, line.product_id.uom_id.id)
weight += (line.product_id.weight or 0.0) * q
volume += (line.product_id.volume or 0.0) * q
quantity += q
total = order.amount_total or 0.0
return self.get_price_from_picking(cr, uid, id, total,weight, volume, quantity, context=context)
def get_price_from_picking(self, cr, uid, id, total, weight, volume, quantity, context=None):

View File

@ -42,6 +42,7 @@ this event.
'data': [
'event_sale_view.xml',
'event_sale_data.xml',
'security/ir.model.access.csv',
],
'demo': ['event_demo.xml'],
'test': ['test/confirm.yml'],

View File

@ -1,18 +1,19 @@
<?xml version="1.0"?>
<openerp>
<data>
<record model="ir.ui.view" id="event_sale_product_form">
<field name="model">product.product</field>
<field name="inherit_id" ref="product.product_normal_form_view" />
<record model="ir.ui.view" id="event_sale_product_template_form">
<field name="model">product.template</field>
<field name="inherit_id" ref="product.product_template_form_view" />
<field name="arch" type="xml">
<div name="options" position="after">
<field name="event_ok" on_change="onchange_event_ok(type, event_ok, context)"
attrs="{'readonly': [('is_only_child', '=', False)]}"/>
<label for="event_ok"/>
<div name="options" position="inside">
<div>
<field name="event_ok" on_change="onchange_event_ok(type, event_ok, context)"/>
<label for="event_ok"/>
</div>
</div>
<field name='type' position="after">
<field name="event_type_id" attrs="{'invisible': [('event_ok', '=', False)],
'readonly': [('is_only_child', '=', False)]}"/>
<field name="event_type_id" attrs="{'invisible': [('event_ok', '=', False)]}"/>
</field>
</field>
</record>
@ -50,20 +51,6 @@
</field>
</record>
<record model="ir.ui.view" id="event_sale_product_template_form">
<field name="model">product.template</field>
<field name="inherit_id" ref="product.product_template_form_view" />
<field name="arch" type="xml">
<div name="options" position="inside">
<field name="event_ok" on_change="onchange_event_ok(type, event_ok, context)"/>
<label for="event_ok"/>
</div>
<field name='company_id' position="after">
<field name="event_type_id" attrs="{'invisible': [('event_ok', '=', False)]}"/>
</field>
</field>
</record>
<record model="ir.ui.view" id="event_order_line">
<field name="name">event.sale.order</field>
<field name="model">sale.order</field>

View File

@ -0,0 +1,3 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_event_event_ticket_user,event.event.ticket.user,event_sale.model_event_event_ticket,event.group_event_user,1,0,0,0
access_event_event_ticket_admin,event.event.ticket.admin,event_sale.model_event_event_ticket,event.group_event_manager,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_event_event_ticket_user event.event.ticket.user event_sale.model_event_event_ticket event.group_event_user 1 0 0 0
3 access_event_event_ticket_admin event.event.ticket.admin event_sale.model_event_event_ticket event.group_event_manager 1 1 1 1

View File

@ -392,8 +392,8 @@ class hr_expense_expense(osv.osv):
return result
class product_product(osv.osv):
_inherit = "product.product"
class product_template(osv.osv):
_inherit = "product.template"
_columns = {
'hr_expense_ok': fields.boolean('Can be Expensed', help="Specify if the product can be selected in an HR expense line."),
}

View File

@ -191,13 +191,15 @@
<record id="view_product_hr_expense_form" model="ir.ui.view">
<field name="name">product.product.expense.form</field>
<field name="model">product.product</field>
<field name="inherit_id" ref="product.product_normal_form_view"/>
<field name="name">product.template.expense.form</field>
<field name="model">product.template</field>
<field name="inherit_id" ref="product.product_template_form_view"/>
<field name="arch" type="xml">
<div name="options" position="inside">
<field name="hr_expense_ok"/>
<label for="hr_expense_ok"/>
<div>
<field name="hr_expense_ok"/>
<label for="hr_expense_ok"/>
</div>
</div>
</field>
</record>

View File

@ -19,7 +19,7 @@
<tr>
<td><strong>Address</strong></td>
<td colspan="3">
<div t-filed="o.employee_id.address_home_id"
<div t-field="o.employee_id.address_home_id"
t-field-options='{"widget": "contact", "fields": ["address", "name", "phone", "fax"], "no_marker": true}'/>
</td>
</tr>

View File

@ -114,6 +114,15 @@ class hr_applicant(osv.Model):
return int(department_ids[0][0])
return None
def _get_default_company_id(self, cr, uid, department_id=None, context=None):
company_id = False
if department_id:
department = self.pool['hr.department'].browse(cr, uid, department_id, context=context)
company_id = department.company_id.id if department and department.company_id else False
if not company_id:
company_id = self.pool['res.company']._company_default_get(cr, uid, 'hr.applicant', context=context)
return company_id
def _read_group_stage_ids(self, cr, uid, ids, domain, read_group_order=None, access_rights_uid=None, context=None):
access_rights_uid = access_rights_uid or uid
stage_obj = self.pool.get('hr.recruitment.stage')
@ -231,7 +240,7 @@ class hr_applicant(osv.Model):
'user_id': lambda s, cr, uid, c: uid,
'stage_id': lambda s, cr, uid, c: s._get_default_stage_id(cr, uid, c),
'department_id': lambda s, cr, uid, c: s._get_default_department_id(cr, uid, c),
'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(cr, uid, 'hr.applicant', context=c),
'company_id': lambda s, cr, uid, c: s._get_default_company_id(cr, uid, s._get_default_department_id(cr, uid, c), c),
'color': 0,
'date_last_stage_update': fields.datetime.now,
}

View File

@ -8,7 +8,7 @@
<div class="oe_structure"/>
<div class="row">
<div class="col-xs-4 col-xs-offset-7">
<div class="col-xs-5 col-xs-offset-7">
<div t-field="user.partner_id"
t-field-options='{"widget": "contact", "fields": ["address", "name"], "no_marker": true}' />
</div>

View File

@ -669,9 +669,10 @@ class mail_thread(osv.AbstractModel):
def message_get_default_recipients(self, cr, uid, ids, context=None):
if context and context.get('thread_model') and context['thread_model'] in self.pool and context['thread_model'] != self._name:
sub_ctx = dict(context)
sub_ctx.pop('thread_model')
return self.pool[context['thread_model']].message_get_default_recipients(cr, uid, ids, context=sub_ctx)
if hasattr(self.pool[context['thread_model']], 'message_get_default_recipients'):
sub_ctx = dict(context)
sub_ctx.pop('thread_model')
return self.pool[context['thread_model']].message_get_default_recipients(cr, uid, ids, context=sub_ctx)
res = {}
for record in self.browse(cr, SUPERUSER_ID, ids, context=context):
recipient_ids, email_to, email_cc = set(), False, False

View File

@ -919,15 +919,45 @@ openerp.mail = function (session) {
this.$('.oe_mail_expand').on('click', this.on_expand);
this.$('.oe_mail_reduce').on('click', this.on_expand);
this.$('.oe_mail_action_model').on('click', this.on_record_clicked);
this.$('.oe_mail_action_author').on('click', this.on_record_author_clicked);
},
on_record_clicked: function (event) {
event.preventDefault();
var self = this;
var state = {
'model': this.model,
'id': this.res_id,
'title': this.record_name
};
session.webclient.action_manager.do_push_state(state);
this.context.params = {
model: this.model,
res_id: this.res_id,
};
this.thread.ds_thread.call("message_redirect_action", {context: this.context}).then(function(action){
self.do_action(action);
});
},
on_record_author_clicked: function (event) {
event.preventDefault();
var partner_id = $(event.target).data('partner');
var state = {
'model': 'res.partner',
'id': partner_id,
'title': this.record_name
};
session.webclient.action_manager.do_push_state(state);
var action = {
type:'ir.actions.act_window',
view_type: 'form',
view_mode: 'form',
res_model: 'res.partner',
views: [[false, 'form']],
res_id: partner_id,
}
this.do_action(action);
},
/* Call the on_compose_message on the thread of this message. */

View File

@ -273,7 +273,7 @@
<t t-if="widget.attachment_ids.length > 0">
<div class="oe_msg_attachment_list"></div>
</t>
<a t-if="widget.author_id and widget.options.show_link and widget.author_id[0]" t-attf-href="#model=res.partner&amp;id=#{widget.author_id[0]}"><t t-esc="widget.author_id[2]"/></a>
<a t-if="widget.author_id and widget.options.show_link and widget.author_id[0]" t-attf-href="#model=res.partner&amp;id=#{widget.author_id[0]}" t-att-data-partner="widget.author_id[0]" class="oe_mail_action_author"><t t-esc="widget.author_id[2]"/></a>
<span t-if="widget.author_id and (!widget.options.show_link or !widget.author_id[0])"><t t-esc="widget.author_id[2]"/></span>
<t t-if="widget.type == 'notification'">
updated document
@ -293,7 +293,7 @@
<t t-if="widget.type == 'notification' or ( (widget.type == 'email' or widget.type == 'comment') and (widget.subtype or widget.partner_ids.length > 0))"
t-foreach="widget.partner_ids.slice(0, 3)" t-as="partner">
<span t-attf-class="oe_partner_follower">
<a t-if="widget.options.show_link" t-attf-href="#model=res.partner&amp;id=#{partner[0]}"><t t-esc="partner[1]"/></a>
<a t-if="widget.options.show_link" t-attf-href="#model=res.partner&amp;id=#{partner[0]}" t-att-data-partner="partner[0]" class="oe_mail_action_author"><t t-esc="partner[1]"/></a>
<t t-if="!widget.options.show_link" t-esc="partner[1]"/>
</span>
<t t-if="!partner_last">,</t>

View File

@ -4,10 +4,9 @@ from datetime import datetime
from dateutil import relativedelta
import json
import random
import urllib
import urlparse
from openerp import tools
from openerp.exceptions import Warning
from openerp.tools.safe_eval import safe_eval as eval
from openerp.tools.translate import _
from openerp.osv import osv, fields
@ -62,6 +61,12 @@ class MassMailingContact(osv.Model):
rec_id = self.create(cr, uid, {'name': name, 'email': email}, context=context)
return self.name_get(cr, uid, [rec_id], context)[0]
def message_get_default_recipients(self, cr, uid, ids, context=None):
res = {}
for record in self.browse(cr, uid, ids, context=context):
res[record.id] = {'partner_ids': [], 'email_to': record.email, 'email_cc': False}
return res
class MassMailingList(osv.Model):
"""Model of a contact list. """
@ -549,6 +554,8 @@ class MassMailing(osv.Model):
for mailing in self.browse(cr, uid, ids, context=context):
# instantiate an email composer + send emails
res_ids = self.get_recipients(cr, uid, mailing, context=context)
if not res_ids:
raise Warning('Please select recipients.')
comp_ctx = dict(context, active_ids=res_ids)
composer_values = {
'author_id': author_id,

View File

@ -462,7 +462,7 @@ class Product(osv.osv):
return super(Product,self).fields_view_get(cr, user, view_id, view_type, context, toolbar, submenu)
'''Product'''
_inherit = 'product.product'
_inherit = 'product.template'
_columns = {
'membership': fields.boolean('Membership', help='Check if the product is eligible for membership.'),
'membership_date_from': fields.date('Membership Start Date', help='Date from which membership becomes active.'),

View File

@ -8,7 +8,7 @@
<record model="ir.ui.view" id="membership_product_search_form_view">
<field name="name">membership.product.search.form</field>
<field name="model">product.product</field>
<field name="model">product.template</field>
<field name="arch" type="xml">
<search string="Membership Products">
<field name="name" filter_domain="['|',('name','ilike',self),('code','ilike',self)]" string="Membership Product"/>
@ -24,10 +24,10 @@
<record model="ir.ui.view" id="membership_products_tree">
<field name="name">Membership products</field>
<field name="model">product.product</field>
<field name="model">product.template</field>
<field name="priority">50</field>
<field name="arch" type="xml">
<tree string="Membership products">
<field name="code"/>
<field name="name"/>
<field name="membership_date_from"/>
<field name="membership_date_to"/>
@ -41,7 +41,8 @@
<record model="ir.ui.view" id="membership_products_form">
<field name="name">Membership Products</field>
<field name="model">product.product</field>
<field name="model">product.template</field>
<field name="priority">50</field>
<field name="arch" type="xml">
<form string="Membership products" version="7.0">
<sheet>
@ -81,31 +82,27 @@
</field>
</record>
<record model="ir.ui.view" id="product_normal_form_view">
<record model="ir.ui.view" id="product_template_form_view">
<field name="name">Membership Products</field>
<field name="model">product.product</field>
<field name="model">product.template</field>
<field name="priority">6</field>
<field name="inherit_id" ref="product.product_normal_form_view"/>
<field name="inherit_id" ref="product.product_template_form_view"/>
<field name="arch" type="xml">
<page string="Accounting" position="after">
<page string="Membership">
<group col="1">
<group>
<field name="membership" readonly="0"/>
</group>
<group attrs="{'invisible':[('membership','=',False)]}">
<field name="membership_date_from" readonly="0" attrs="{'required':[('membership','=',True)]}"/>
<field name="membership_date_to" readonly="0" attrs="{'required':[('membership','=',True)]}"/>
</group>
</group>
</page>
</page>
<field name="type" position="after">
<field name="membership" readonly="0" attrs="{'invisible': [('type', '!=', 'service')]}"/>
</field>
<field name="description" position="before">
<group attrs="{'invisible':[('membership','=',False)]}">
<field name="membership_date_from" readonly="0" attrs="{'required':[('membership','=',True)]}"/>
<field name="membership_date_to" readonly="0" attrs="{'required':[('membership','=',True)]}"/>
</group>
</field>
</field>
</record>
<record model="ir.actions.act_window" id="action_membership_products">
<field name="name">Membership Products</field>
<field name="res_model">product.product</field>
<field name="res_model">product.template</field>
<field name="domain">[('membership','=',True), ('type', '=', 'service')]</field>
<field name="context">{'membership':True, 'type':'service', 'default_membership': True, 'default_type': 'service'}</field>
<field name="search_view_id" ref="membership_product_search_form_view"/>

View File

@ -178,106 +178,238 @@ class mrp_bom(osv.osv):
bom_parent = bom_obj.browse(cr, uid, bom_id, context=context)
for bom in self.browse(cr, uid, ids, context=context):
if (bom_parent) or (bom.id == bom_id):
result[bom.id] = map(lambda x: x.id, bom.bom_lines)
result[bom.id] = map(lambda x: x.id, bom.bom_line_ids)
else:
result[bom.id] = []
if bom.bom_lines:
if bom.bom_line_ids:
continue
ok = ((name=='child_complete_ids'))
if (bom.type=='phantom' or ok):
sids = bom_obj.search(cr, uid, [('bom_id','=',False),('product_id','=',bom.product_id.id)])
sids = bom_obj.search(cr, uid, [('product_tmpl_id','=',bom.product_tmpl_id.id)])
if sids:
bom2 = bom_obj.browse(cr, uid, sids[0], context=context)
result[bom.id] += map(lambda x: x.id, bom2.bom_lines)
result[bom.id] += map(lambda x: x.id, bom2.bom_line_ids)
return result
_columns = {
'name': fields.char('Name', size=64),
'code': fields.char('Reference', size=16),
'active': fields.boolean('Active', help="If the active field is set to False, it will allow you to hide the bills of material without removing it."),
'type': fields.selection([('normal', 'Normal BoM'), ('phantom', 'Sets / Phantom')], 'BoM Type', required=True,
help= "If a by-product is used in several products, it can be useful to create its own BoM. "\
"Though if you don't want separated production orders for this by-product, select Set/Phantom as BoM type. "\
"If a Phantom BoM is used for a root product, it will be sold and shipped as a set of components, instead of being produced."),
'date_start': fields.date('Valid From', help="Validity of this BoM or component. Keep empty if it's always valid."),
'date_stop': fields.date('Valid Until', help="Validity of this BoM or component. Keep empty if it's always valid."),
'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of bills of material."),
'type': fields.selection([('normal', 'Normal'), ('phantom', 'Set')], 'BoM Type', required=True,
help= "Set: When processing a sales order for this product, the delivery order will contain the raw materials, instead of the finished product."),
'position': fields.char('Internal Reference', size=64, help="Reference to a position in an external plan."),
'product_id': fields.many2one('product.product', 'Product', required=True),
'product_uos_qty': fields.float('Product UOS Qty'),
'product_uos': fields.many2one('product.uom', 'Product UOS', help="Product UOS (Unit of Sale) is the unit of measurement for the invoicing and promotion of stock."),
'product_tmpl_id': fields.many2one('product.template', 'Product', required=True),
'product_id': fields.many2one('product.product', 'Product Variant',
domain="[('product_tmpl_id','=',product_tmpl_id)]",
help="If a product variant is defined the BOM is available only for this product."),
'bom_line_ids': fields.one2many('mrp.bom.line', 'bom_id', 'BoM Lines'),
'product_qty': fields.float('Product Quantity', required=True, digits_compute=dp.get_precision('Product Unit of Measure')),
'product_uom': fields.many2one('product.uom', 'Product Unit of Measure', required=True, help="Unit of Measure (Unit of Measure) is the unit of measurement for the inventory control"),
'date_start': fields.date('Valid From', help="Validity of this BoM. Keep empty if it's always valid."),
'date_stop': fields.date('Valid Until', help="Validity of this BoM. Keep empty if it's always valid."),
'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of bills of material."),
'routing_id': fields.many2one('mrp.routing', 'Routing', help="The list of operations (list of work centers) to produce the finished product. "\
"The routing is mainly used to compute work center costs during operations and to plan future loads on work centers based on production planning."),
'product_rounding': fields.float('Product Rounding', help="Rounding applied on the product quantity."),
'product_efficiency': fields.float('Manufacturing Efficiency', required=True, help="A factor of 0.9 means a loss of 10% within the production process."),
'bom_lines': fields.one2many('mrp.bom', 'bom_id', 'BoM Lines'),
'bom_id': fields.many2one('mrp.bom', 'Parent BoM', ondelete='cascade', select=True),
'routing_id': fields.many2one('mrp.routing', 'Routing', help="The list of operations (list of work centers) to produce the finished product. The routing is mainly used to compute work center costs during operations and to plan future loads on work centers based on production planning."),
'property_ids': fields.many2many('mrp.property', 'mrp_bom_property_rel', 'bom_id', 'property_id', 'Properties'),
'product_efficiency': fields.float('Manufacturing Efficiency', required=True, help="A factor of 0.9 means a loss of 10% during the production process."),
'property_ids': fields.many2many('mrp.property', string='Properties'),
'child_complete_ids': fields.function(_child_compute, relation='mrp.bom', string="BoM Hierarchy", type='many2many'),
'company_id': fields.many2one('res.company', 'Company', required=True),
}
def _get_uom_id(self, cr, uid, *args):
return self.pool["product.uom"].search(cr, uid, [], limit=1, order='id')[0]
_defaults = {
'active': lambda *a: 1,
'product_efficiency': lambda *a: 1.0,
'product_qty': lambda *a: 1.0,
'product_efficiency': lambda *a: 1.0,
'product_rounding': lambda *a: 0.0,
'type': lambda *a: 'normal',
'product_uom': _get_uom_id,
'company_id': lambda self, cr, uid, c: self.pool.get('res.company')._company_default_get(cr, uid, 'mrp.bom', context=c),
}
_order = "sequence"
_parent_name = "bom_id"
_sql_constraints = [
('bom_qty_zero', 'CHECK (product_qty>0)', 'All product quantities must be greater than 0.\n' \
'You should install the mrp_byproduct module if you want to manage extra products on BoMs !'),
]
def _check_recursion(self, cr, uid, ids, context=None):
level = 100
while len(ids):
cr.execute('select distinct bom_id from mrp_bom where id IN %s', (tuple(ids),))
ids = filter(None, map(lambda x: x[0], cr.fetchall()))
if not level:
return False
level -= 1
return True
def _bom_find(self, cr, uid, product_uom, product_tmpl_id=None, product_id=None, properties=None):
""" Finds BoM for particular product and product uom.
@param product_tmpl_id: Selected product.
@param product_uom: Unit of measure of a product.
@param properties: List of related properties.
@return: False or BoM id.
"""
if properties is None:
properties = []
domain = None
if product_id:
domain = ['|',('product_id', '=', product_id),('product_tmpl_id.product_variant_ids', '=', product_id)]
else:
domain = [('product_id', '=', False), ('product_tmpl_id', '=', product_tmpl_id)]
if product_uom:
domain += [('product_uom','=',product_uom)]
domain = domain + [ '|', ('date_start', '=', False), ('date_start', '<=', time.strftime(DEFAULT_SERVER_DATETIME_FORMAT)),
'|', ('date_stop', '=', False), ('date_stop', '>=', time.strftime(DEFAULT_SERVER_DATETIME_FORMAT))]
ids = self.search(cr, uid, domain)
for bom in self.pool.get('mrp.bom').browse(cr, uid, ids):
if not set(map(int,bom.property_ids or [])) - set(properties or []):
return bom.id
return False
def _check_product(self, cr, uid, ids, context=None):
all_prod = []
boms = self.browse(cr, uid, ids, context=context)
def check_bom(boms):
res = True
for bom in boms:
if bom.product_id.id in all_prod:
res = res and False
all_prod.append(bom.product_id.id)
lines = bom.bom_lines
if lines:
res = res and check_bom([bom_id for bom_id in lines if bom_id not in boms])
def _bom_explode(self, cr, uid, bom, product, factor, properties=None, level=0, routing_id=False, previous_products=None, master_bom=None):
""" Finds Products and Work Centers for related BoM for manufacturing order.
@param bom: BoM of particular product template.
@param product: Select a particular variant of the BoM. If False use BoM without variants.
@param factor: Factor of product UoM.
@param properties: A List of properties Ids.
@param level: Depth level to find BoM lines starts from 10.
@param previous_products: List of product previously use by bom explore to avoid recursion
@param master_bom: When recursion, used to display the name of the master bom
@return: result: List of dictionaries containing product details.
result2: List of dictionaries containing Work Center details.
"""
routing_obj = self.pool.get('mrp.routing')
all_prod = [] + (previous_products or [])
master_bom = master_bom or bom
def _factor(factor, product_efficiency, product_rounding):
factor = factor / (product_efficiency or 1.0)
factor = _common.ceiling(factor, product_rounding)
if factor < product_rounding:
factor = product_rounding
return factor
factor = _factor(factor, bom.product_efficiency, bom.product_rounding)
result = []
result2 = []
routing = (routing_id and routing_obj.browse(cr, uid, routing_id)) or bom.routing_id or False
if routing:
for wc_use in routing.workcenter_lines:
wc = wc_use.workcenter_id
d, m = divmod(factor, wc_use.workcenter_id.capacity_per_cycle)
mult = (d + (m and 1.0 or 0.0))
cycle = mult * wc_use.cycle_nbr
result2.append({
'name': tools.ustr(wc_use.name) + ' - ' + tools.ustr(bom.product_tmpl_id.name_get()[0][1]),
'workcenter_id': wc.id,
'sequence': level + (wc_use.sequence or 0),
'cycle': cycle,
'hour': float(wc_use.hour_nbr * mult + ((wc.time_start or 0.0) + (wc.time_stop or 0.0) + cycle * (wc.time_cycle or 0.0)) * (wc.time_efficiency or 1.0)),
})
for bom_line_id in bom.bom_line_ids:
if bom_line_id.date_start and bom_line_id.date_start > time.strftime(DEFAULT_SERVER_DATETIME_FORMAT) or \
bom_line_id.date_stop and bom_line_id.date_stop > time.strftime(DEFAULT_SERVER_DATETIME_FORMAT):
continue
# check properties
if set(map(int,bom_line_id.property_ids or [])) - set(properties or []):
continue
# all bom_line_id variant values must be in the product
if bom_line_id.attribute_value_ids:
if not product or (set(map(int,bom_line_id.attribute_value_ids or [])) - set(map(int,product.attribute_value_ids))):
continue
if bom_line_id.product_id.id in all_prod:
raise osv.except_osv(_('Invalid Action!'), _('BoM "%s" contains a BoM line with a product recursion: "%s".') % (master_bom.name,bom_line_id.product_id.name_get()[0][1]))
all_prod.append(bom_line_id.product_id.id)
if bom_line_id.type != "phantom":
result.append({
'name': bom_line_id.product_id.name,
'product_id': bom_line_id.product_id.id,
'product_qty': _factor(bom_line_id.product_qty * factor, bom_line_id.product_efficiency, bom_line_id.product_rounding),
'product_uom': bom_line_id.product_uom.id,
'product_uos_qty': bom_line_id.product_uos and bom_line_id.product_uos_qty * factor or False,
'product_uos': bom_line_id.product_uos and bom_line_id.product_uos.id or False,
})
else:
bom_id = self._bom_find(cr, uid, bom_line_id.product_uom.id, product_id=bom_line_id.product_id.id, properties=properties)
bom2 = self.browse(cr, uid, bom_id)
if bom2:
res = self._bom_explode(cr, uid, bom2, bom_line_id.product_id, factor,
properties=properties, level=level + 10, previous_products=all_prod, master_bom=master_bom)
result = result + res[0]
result2 = result2 + res[1]
else:
raise osv.except_osv(_('Invalid Action!'), _('BoM "%s" contains a phantom BoM line but the product "%s" don\'t have any BoM defined.') % (master_bom.name,bom_line_id.product_id.name_get()[0][1]))
return result, result2
def copy_data(self, cr, uid, id, default=None, context=None):
if default is None:
default = {}
bom_data = self.read(cr, uid, id, [], context=context)
default.update(name=_("%s (copy)") % (bom_data['name']))
return super(mrp_bom, self).copy_data(cr, uid, id, default, context=context)
def onchange_uom(self, cr, uid, ids, product_tmpl_id, product_uom, context=None):
res = {'value': {}}
if not product_uom or not product_tmpl_id:
return res
return check_bom(boms)
product = self.pool.get('product.template').browse(cr, uid, product_tmpl_id, context=context)
uom = self.pool.get('product.uom').browse(cr, uid, product_uom, context=context)
if uom.category_id.id != product.uom_id.category_id.id:
res['warning'] = {'title': _('Warning'), 'message': _('The Product Unit of Measure you chose has a different category than in the product form.')}
res['value'].update({'product_uom': product.uom_id.id})
return res
_constraints = [
(_check_recursion, 'Error ! You cannot create recursive BoM.', ['parent_id']),
(_check_product, 'BoM line product should not be same as BoM product.', ['product_id']),
]
def onchange_product_id(self, cr, uid, ids, product_id, name, product_qty=0, context=None):
def onchange_product_tmpl_id(self, cr, uid, ids, product_tmpl_id, product_qty=0, context=None):
""" Changes UoM and name if product_id changes.
@param name: Name of the field
@param product_id: Changed product_id
@return: Dictionary of changed values
"""
res = {}
if product_id:
prod = self.pool.get('product.product').browse(cr, uid, product_id, context=context)
res['value'] = {'name': prod.name, 'product_uom': prod.uom_id.id, 'product_uos_qty': 0, 'product_uos': False}
if prod.uos_id.id:
res['value']['product_uos_qty'] = product_qty * prod.uos_coeff
res['value']['product_uos'] = prod.uos_id.id
if product_tmpl_id:
prod = self.pool.get('product.template').browse(cr, uid, product_tmpl_id, context=context)
res['value'] = {
'name': prod.name,
'product_uom': prod.uom_id.id,
}
return res
class mrp_bom_line(osv.osv):
_name = 'mrp.bom.line'
_order = "sequence"
_columns = {
'type': fields.selection([('normal', 'Normal'), ('phantom', 'Phantom')], 'BoM Line Type', required=True,
help="Phantom: this product line will not appear in the raw materials of manufacturing orders,"
"it will be directly replaced by the raw materials of its own BoM, without triggering"
"an extra manufacturing order."),
'product_id': fields.many2one('product.product', 'Product', required=True),
'product_uos_qty': fields.float('Product UOS Qty'),
'product_uos': fields.many2one('product.uom', 'Product UOS', help="Product UOS (Unit of Sale) is the unit of measurement for the invoicing and promotion of stock."),
'product_qty': fields.float('Product Quantity', required=True, digits_compute=dp.get_precision('Product Unit of Measure')),
'product_uom': fields.many2one('product.uom', 'Product Unit of Measure', required=True,
help="Unit of Measure (Unit of Measure) is the unit of measurement for the inventory control"),
'date_start': fields.date('Valid From', help="Validity of component. Keep empty if it's always valid."),
'date_stop': fields.date('Valid Until', help="Validity of component. Keep empty if it's always valid."),
'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying."),
'routing_id': fields.many2one('mrp.routing', 'Routing', help="The list of operations (list of work centers) to produce the finished product. The routing is mainly used to compute work center costs during operations and to plan future loads on work centers based on production planning."),
'product_rounding': fields.float('Product Rounding', help="Rounding applied on the product quantity."),
'product_efficiency': fields.float('Manufacturing Efficiency', required=True, help="A factor of 0.9 means a loss of 10% within the production process."),
'property_ids': fields.many2many('mrp.property', string='Properties'),
'bom_id': fields.many2one('mrp.bom', 'Parent BoM', ondelete='cascade', select=True, required=True),
'attribute_value_ids': fields.many2many('product.attribute.value', string='Variants', help="BOM Product Variants needed form apply this line."),
}
def _get_uom_id(self, cr, uid, *args):
return self.pool["product.uom"].search(cr, uid, [], limit=1, order='id')[0]
_defaults = {
'product_qty': lambda *a: 1.0,
'product_efficiency': lambda *a: 1.0,
'product_rounding': lambda *a: 0.0,
'type': lambda *a: 'normal',
'product_uom': _get_uom_id,
}
_sql_constraints = [
('bom_qty_zero', 'CHECK (product_qty>0)', 'All product quantities must be greater than 0.\n' \
'You should install the mrp_byproduct module if you want to manage extra products on BoMs !'),
]
def onchange_uom(self, cr, uid, ids, product_id, product_uom, context=None):
res = {'value': {}}
if not product_uom or not product_id:
@ -289,96 +421,23 @@ class mrp_bom(osv.osv):
res['value'].update({'product_uom': product.uom_id.id})
return res
def _bom_find(self, cr, uid, product_id, product_uom, properties=None):
""" Finds BoM for particular product and product uom.
@param product_id: Selected product.
@param product_uom: Unit of measure of a product.
@param properties: List of related properties.
@return: False or BoM id.
def onchange_product_id(self, cr, uid, ids, product_id, product_qty=0, context=None):
""" Changes UoM if product_id changes.
@param product_id: Changed product_id
@return: Dictionary of changed values
"""
if properties is None:
properties = []
domain = [('product_id', '=', product_id), ('bom_id', '=', False),
'|', ('date_start', '=', False), ('date_start', '<=', time.strftime(DEFAULT_SERVER_DATETIME_FORMAT)),
'|', ('date_stop', '=', False), ('date_stop', '>=', time.strftime(DEFAULT_SERVER_DATETIME_FORMAT))]
ids = self.search(cr, uid, domain)
max_prop = 0
result = False
for bom in self.pool.get('mrp.bom').browse(cr, uid, ids):
prop = 0
for prop_id in bom.property_ids:
if prop_id.id in properties:
prop += 1
if (prop > max_prop) or ((max_prop == 0) and not result):
result = bom.id
max_prop = prop
return result
def _bom_explode(self, cr, uid, bom, factor, properties=None, addthis=False, level=0, routing_id=False):
""" Finds Products and Work Centers for related BoM for manufacturing order.
@param bom: BoM of particular product.
@param factor: Factor of product UoM.
@param properties: A List of properties Ids.
@param addthis: If BoM found then True else False.
@param level: Depth level to find BoM lines starts from 10.
@return: result: List of dictionaries containing product details.
result2: List of dictionaries containing Work Center details.
"""
routing_obj = self.pool.get('mrp.routing')
factor = factor / (bom.product_efficiency or 1.0)
factor = _common.ceiling(factor, bom.product_rounding)
if factor < bom.product_rounding:
factor = bom.product_rounding
result = []
result2 = []
phantom = False
if bom.type == 'phantom' and not bom.bom_lines:
newbom = self._bom_find(cr, uid, bom.product_id.id, bom.product_uom.id, properties)
if newbom:
res = self._bom_explode(cr, uid, self.browse(cr, uid, [newbom])[0], factor * bom.product_qty, properties, addthis=True, level=level + 10)
result = result + res[0]
result2 = result2 + res[1]
phantom = True
else:
phantom = False
if not phantom:
if addthis and not bom.bom_lines:
result.append({
'name': bom.product_id.name,
'product_id': bom.product_id.id,
'product_qty': bom.product_qty * factor,
'product_uom': bom.product_uom.id,
'product_uos_qty': bom.product_uos and bom.product_uos_qty * factor or False,
'product_uos': bom.product_uos and bom.product_uos.id or False,
})
routing = (routing_id and routing_obj.browse(cr, uid, routing_id)) or bom.routing_id or False
if routing:
for wc_use in routing.workcenter_lines:
wc = wc_use.workcenter_id
d, m = divmod(factor, wc_use.workcenter_id.capacity_per_cycle)
mult = (d + (m and 1.0 or 0.0))
cycle = mult * wc_use.cycle_nbr
result2.append({
'name': tools.ustr(wc_use.name) + ' - ' + tools.ustr(bom.product_id.name),
'workcenter_id': wc.id,
'sequence': level + (wc_use.sequence or 0),
'cycle': cycle,
'hour': float(wc_use.hour_nbr * mult + ((wc.time_start or 0.0) + (wc.time_stop or 0.0) + cycle * (wc.time_cycle or 0.0)) * (wc.time_efficiency or 1.0)),
})
for bom2 in bom.bom_lines:
res = self._bom_explode(cr, uid, bom2, factor, properties, addthis=True, level=level + 10)
result = result + res[0]
result2 = result2 + res[1]
return result, result2
def copy_data(self, cr, uid, id, default=None, context=None):
if default is None:
default = {}
bom_data = self.read(cr, uid, id, [], context=context)
default.update(name=_("%s (copy)") % (bom_data['name']), bom_id=False)
return super(mrp_bom, self).copy_data(cr, uid, id, default, context=context)
res = {}
if product_id:
prod = self.pool.get('product.product').browse(cr, uid, product_id, context=context)
res['value'] = {
'product_uom': prod.uom_id.id,
'product_uos_qty': 0,
'product_uos': False
}
if prod.uos_id.id:
res['value']['product_uos_qty'] = product_qty * prod.uos_coeff
res['value']['product_uos'] = prod.uos_id.id
return res
class mrp_production(osv.osv):
"""
@ -475,7 +534,7 @@ class mrp_production(osv.osv):
'date_planned': fields.datetime('Scheduled Date', required=True, select=1, readonly=True, states={'draft': [('readonly', False)]}),
'date_start': fields.datetime('Start Date', select=True, readonly=True),
'date_finished': fields.datetime('End Date', select=True, readonly=True),
'bom_id': fields.many2one('mrp.bom', 'Bill of Material', domain=[('bom_id', '=', False)], readonly=True, states={'draft': [('readonly', False)]},
'bom_id': fields.many2one('mrp.bom', 'Bill of Material', readonly=True, states={'draft': [('readonly', False)]},
help="Bill of Materials allow you to define the list of required raw materials to make a finished product."),
'routing_id': fields.many2one('mrp.routing', string='Routing', on_delete='set null', readonly=True, states={'draft': [('readonly', False)]},
help="The list of operations (list of work centers) to produce the finished product. The routing is mainly used to compute work center costs during operations and to plan future loads on work centers based on production plannification."),
@ -586,7 +645,7 @@ class mrp_production(osv.osv):
}}
bom_obj = self.pool.get('mrp.bom')
product = self.pool.get('product.product').browse(cr, uid, product_id, context=context)
bom_id = bom_obj._bom_find(cr, uid, product.id, product.uom_id and product.uom_id.id, [])
bom_id = bom_obj._bom_find(cr, uid, product.uom_id and product.uom_id.id, product_id=product.id, properties=[])
routing_id = False
if bom_id:
bom_point = bom_obj.browse(cr, uid, bom_id, context=context)
@ -635,7 +694,7 @@ class mrp_production(osv.osv):
bom_point = production.bom_id
bom_id = production.bom_id.id
if not bom_point:
bom_id = bom_obj._bom_find(cr, uid, production.product_id.id, production.product_uom.id, properties)
bom_id = bom_obj._bom_find(cr, uid, production.product_uom.id, product_id=production.product_id.id, properties=properties)
if bom_id:
bom_point = bom_obj.browse(cr, uid, bom_id)
routing_id = bom_point.routing_id.id or False
@ -646,9 +705,8 @@ class mrp_production(osv.osv):
# get components and workcenter_lines from BoM structure
factor = uom_obj._compute_qty(cr, uid, production.product_uom.id, production.product_qty, bom_point.product_uom.id)
res = bom_obj._bom_explode(cr, uid, bom_point, factor / bom_point.product_qty, properties, routing_id=production.routing_id.id)
results = res[0] # product_lines
results2 = res[1] # workcenter_lines
# product_lines, workcenter_lines
results, results2 = bom_obj._bom_explode(cr, uid, bom_point, production.product_id, factor / bom_point.product_qty, properties, routing_id=production.routing_id.id)
# reset product_lines in production order
for line in results:
line['production_id'] = production.id
@ -777,15 +835,18 @@ class mrp_production(osv.osv):
dicts = {}
# Find product qty to be consumed and consume it
for scheduled in production.product_lines:
consumed_qty = consumed_data.get(scheduled.product_id.id, 0.0)
product_id = scheduled.product_id.id
consumed_qty = consumed_data.get(product_id, 0.0)
# qty available for consume and produce
qty_avail = scheduled.product_qty - consumed_qty
if qty_avail <= 0.0:
# there will be nothing to consume for this raw material
continue
if not dicts.get(scheduled.product_id.id):
dicts[scheduled.product_id.id] = {}
if not dicts.get(product_id):
dicts[product_id] = {}
# total qty of consumed product we need after this consumption
total_consume = ((product_qty + produced_qty) * scheduled.product_qty / production.product_qty)
@ -795,9 +856,8 @@ class mrp_production(osv.osv):
for move in production.move_lines:
if qty <= 0.0:
break
if move.product_id.id != scheduled.product_id.id:
if move.product_id.id != product_id:
continue
product_id = scheduled.product_id.id
q = min(move.product_qty, qty)
quants = quant_obj.quants_get_prefered_domain(cr, uid, move.location_id, scheduled.product_id, q, domain=[('qty', '>', 0.0)],
@ -895,7 +955,8 @@ class mrp_production(osv.osv):
#consumed more in wizard than previously planned
product = self.pool.get('product.product').browse(cr, uid, consume['product_id'], context=context)
extra_move_id = self._make_consume_line_from_data(cr, uid, production, product, product.uom_id.id, remaining_qty, False, 0, context=context)
stock_mov_obj.action_done(cr, uid, [extra_move_id], context=context)
if extra_move_id:
stock_mov_obj.action_done(cr, uid, [extra_move_id], context=context)
self.message_post(cr, uid, production_id, body=_("%s produced") % self._description, context=context)
self.signal_button_produce_done(cr, uid, [production_id])
@ -1111,24 +1172,4 @@ class mrp_production_product_line(osv.osv):
'production_id': fields.many2one('mrp.production', 'Production Order', select=True),
}
class product_product(osv.osv):
_inherit = "product.product"
def _bom_orders_count(self, cr, uid, ids, field_name, arg, context=None):
Bom = self.pool('mrp.bom')
Production = self.pool('mrp.production')
return {
product_id: {
'bom_count': Bom.search_count(cr, uid, [('product_id', '=', product_id), ('bom_id', '=', False)], context=context),
'mo_count': Production.search_count(cr,uid, [('product_id', '=', product_id)], context=context),
'bom_strct': Bom.search_count(cr, uid, [('product_id', '=', product_id), ('bom_id', '=', False)], context=context),
}
for product_id in ids
}
_columns = {
'bom_ids': fields.one2many('mrp.bom', 'product_id', 'Bill of Materials'),
'bom_count': fields.function(_bom_orders_count, string='# Bill of Material', type='integer', multi="_bom_order_count"),
'bom_strct': fields.function(_bom_orders_count, string='# Bill of Material Structure', type='integer', multi="_bom_order_count"),
'mo_count': fields.function(_bom_orders_count, string='# Manufacturing Orders', type='integer', multi="_bom_order_count"),
}
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -245,16 +245,14 @@
<record id="mrp_bom_1" model="mrp.bom">
<field name="name">HDD 500GB</field>
<field name="product_id" ref="product.product_product_18"/>
<field name="product_qty">1</field>
<field name="product_tmpl_id" ref="product.product_product_18_product_template"/>
<field name="product_uom" ref="product.product_uom_unit"/>
<field name="sequence">5</field>
<field name="routing_id" ref="mrp_routing_0"/>
<field name="type">normal</field>
</record>
<record id="mrp_bom_2" model="mrp.bom">
<field name="name">320GB HDD</field>
<record id="mrp_bom_line_1" model="mrp.bom.line">
<field name="product_id" ref="product.product_product_17"/>
<field name="product_qty">2</field>
<field name="product_uom" ref="product.product_uom_unit"/>
@ -263,398 +261,394 @@
<field name="bom_id" ref="mrp_bom_1"/>
</record>
<record id="mrp_bom_3" model="mrp.bom">
<record id="mrp_bom_2" model="mrp.bom">
<field name="name">HDD 2TB on Demand</field>
<field name="product_id" ref="product.product_product_19"/>
<field name="product_qty">1</field>
<field name="product_tmpl_id" ref="product.product_product_19_product_template"/>
<field name="product_uom" ref="product.product_uom_unit"/>
<field name="sequence">5</field>
<field name="routing_id" ref="mrp_routing_1"/>
<field name="type">normal</field>
</record>
<record id="mrp_bom_4" model="mrp.bom">
<field name="name">HDD 500GB</field>
<record id="mrp_bom_line_2" model="mrp.bom.line">
<field name="product_id" ref="product.product_product_18"/>
<field name="product_qty">4</field>
<field name="product_uom" ref="product.product_uom_unit"/>
<field name="sequence">5</field>
<field name="type">normal</field>
<field name="bom_id" ref="mrp_bom_2"/>
</record>
<record id="mrp_bom_3" model="mrp.bom">
<field name="name">HDD on Demand</field>
<field name="product_tmpl_id" ref="product.product_product_19_product_template"/>
<field name="product_uom" ref="product.product_uom_unit"/>
<field name="sequence">10</field>
<field name="type">normal</field>
</record>
<record id="mrp_bom_line_3" model="mrp.bom.line">
<field name="product_id" ref="product.product_product_17"/>
<field name="product_qty">1</field>
<field name="product_uom" ref="product.product_uom_unit"/>
<field name="sequence">5</field>
<field name="type">normal</field>
<field name="bom_id" ref="mrp_bom_3"/>
</record>
<record id="mrp_bom_5" model="mrp.bom">
<field name="name">HDD on Demand</field>
<field name="product_id" ref="product.product_product_19"/>
<field name="product_qty">1</field>
<field name="product_uom" ref="product.product_uom_unit"/>
<field name="sequence">10</field>
<field name="type">normal</field>
</record>
<record id="mrp_bom_6" model="mrp.bom">
<field name="name">320GB HDD</field>
<field name="product_id" ref="product.product_product_17"/>
<field name="product_qty">1</field>
<field name="product_uom" ref="product.product_uom_unit"/>
<field name="sequence">5</field>
<field name="type">normal</field>
<field name="bom_id" ref="mrp_bom_5"/>
</record>
<record id="mrp_bom_7" model="mrp.bom">
<record id="mrp_bom_4" model="mrp.bom">
<field name="name">Laptop Customized</field>
<field name="product_id" ref="product.product_product_27"/>
<field name="product_qty">1</field>
<field name="product_tmpl_id" ref="product.product_product_27_product_template"/>
<field name="product_uom" ref="product.product_uom_unit"/>
<field name="sequence">5</field>
<field name="routing_id" ref="mrp_routing_1"/>
<field name="type">normal</field>
</record>
<record id="mrp_bom_8" model="mrp.bom">
<field name="name">Windows 7 Professional</field>
<record id="mrp_bom_line_4" model="mrp.bom.line">
<field name="product_id" ref="product.product_product_40"/>
<field name="product_qty">1</field>
<field name="product_uom" ref="product.product_uom_unit"/>
<field name="sequence">2</field>
<field name="type">normal</field>
<field name="bom_id" ref="mrp_bom_7"/>
<field name="bom_id" ref="mrp_bom_4"/>
</record>
<record id="mrp_bom_9" model="mrp.bom">
<field name="name">USB Keyboard, QWERTY</field>
<record id="mrp_bom_line_5" model="mrp.bom.line">
<field name="product_id" ref="product.product_product_8"/>
<field name="product_qty">1</field>
<field name="product_uom" ref="product.product_uom_unit"/>
<field name="sequence">4</field>
<field name="type">normal</field>
<field name="bom_id" ref="mrp_bom_7"/>
<field name="bom_id" ref="mrp_bom_4"/>
</record>
<record id="mrp_bom_10" model="mrp.bom">
<field name="name">Mouse, Wireless</field>
<record id="mrp_bom_line_6" model="mrp.bom.line">
<field name="product_id" ref="product.product_product_12"/>
<field name="product_qty">1</field>
<field name="product_uom" ref="product.product_uom_unit"/>
<field name="sequence">6</field>
<field name="type">normal</field>
<field name="bom_id" ref="mrp_bom_7"/>
<field name="bom_id" ref="mrp_bom_4"/>
</record>
<record id="mrp_bom_11" model="mrp.bom">
<record id="mrp_bom_5" model="mrp.bom">
<field name="name">Laptop Customized + Azerty</field>
<field name="product_id" ref="product.product_product_27"/>
<field name="product_qty">1</field>
<field name="product_tmpl_id" ref="product.product_product_27_product_template"/>
<field name="product_uom" ref="product.product_uom_unit"/>
<field name="sequence">10</field>
<field name="type">normal</field>
</record>
<record id="mrp_bom_12" model="mrp.bom">
<field name="name">Windows 7 Professional</field>
<record id="mrp_bom_line_7" model="mrp.bom.line">
<field name="product_id" ref="product.product_product_40"/>
<field name="product_qty">1</field>
<field name="product_uom" ref="product.product_uom_unit"/>
<field name="sequence">2</field>
<field name="type">normal</field>
<field name="bom_id" ref="mrp_bom_11"/>
<field name="bom_id" ref="mrp_bom_5"/>
</record>
<record id="mrp_bom_13" model="mrp.bom">
<field name="name">USB Keyboard, AZERTY</field>
<record id="mrp_bom_line_8" model="mrp.bom.line">
<field name="product_id" ref="product.product_product_9"/>
<field name="product_qty">1</field>
<field name="product_uom" ref="product.product_uom_unit"/>
<field name="sequence">4</field>
<field name="type">normal</field>
<field name="bom_id" ref="mrp_bom_11"/>
<field name="bom_id" ref="mrp_bom_5"/>
</record>
<record id="mrp_bom_14" model="mrp.bom">
<field name="name">Mouse, Laser</field>
<record id="mrp_bom_line_9" model="mrp.bom.line">
<field name="product_id" ref="product.product_product_11"/>
<field name="product_qty">1</field>
<field name="product_uom" ref="product.product_uom_unit"/>
<field name="sequence">6</field>
<field name="type">normal</field>
<field name="bom_id" ref="mrp_bom_11"/>
<field name="bom_id" ref="mrp_bom_5"/>
</record>
<record id="mrp_bom_15" model="mrp.bom">
<record id="mrp_bom_6" model="mrp.bom">
<field name="name">Computer Case-1</field>
<field name="product_id" ref="product.product_product_16"/>
<field name="product_qty">1</field>
<field name="product_tmpl_id" ref="product.product_product_16_product_template"/>
<field name="product_uom" ref="product.product_uom_unit"/>
<field name="sequence">5</field>
<field name="type">normal</field>
</record>
<record id="mrp_bom_16" model="mrp.bom">
<field name="name">Processesor AMD 8-Core</field>
<record id="mrp_bom_line_10" model="mrp.bom.line">
<field name="product_id" ref="product.product_product_23"/>
<field name="product_qty">1</field>
<field name="product_uom" ref="product.product_uom_unit"/>
<field name="sequence">4</field>
<field name="type">normal</field>
<field name="bom_id" ref="mrp_bom_15"/>
<field name="bom_id" ref="mrp_bom_6"/>
</record>
<record id="mrp_bom_17" model="mrp.bom">
<field name="name">Motherboard I9P57</field>
<record id="mrp_bom_line_11" model="mrp.bom.line">
<field name="product_id" ref="product.product_product_20"/>
<field name="product_qty">1</field>
<field name="product_uom" ref="product.product_uom_unit"/>
<field name="sequence">6</field>
<field name="type">normal</field>
<field name="bom_id" ref="mrp_bom_15"/>
<field name="bom_id" ref="mrp_bom_6"/>
</record>
<record id="mrp_bom_18" model="mrp.bom">
<field name="name">HDD 320GB</field>
<record id="mrp_bom_line_12" model="mrp.bom.line">
<field name="product_id" ref="product.product_product_17"/>
<field name="product_qty">1</field>
<field name="product_uom" ref="product.product_uom_unit"/>
<field name="sequence">8</field>
<field name="type">normal</field>
<field name="bom_id" ref="mrp_bom_15"/>
<field name="bom_id" ref="mrp_bom_6"/>
</record>
<record id="mrp_bom_19" model="mrp.bom">
<record id="mrp_bom_7" model="mrp.bom">
<field name="name">Computer Case-2</field>
<field name="product_id" ref="product.product_product_16"/>
<field name="product_qty">1</field>
<field name="product_tmpl_id" ref="product.product_product_16_product_template"/>
<field name="product_uom" ref="product.product_uom_unit"/>
<field name="sequence">10</field>
<field name="type">normal</field>
</record>
<record id="mrp_bom_20" model="mrp.bom">
<field name="name">Processor Core i5 2.70 Ghz</field>
<record id="mrp_bom_line_13" model="mrp.bom.line">
<field name="product_id" ref="product.product_product_22"/>
<field name="product_qty">1</field>
<field name="product_uom" ref="product.product_uom_unit"/>
<field name="sequence">2</field>
<field name="type">normal</field>
<field name="bom_id" ref="mrp_bom_19"/>
<field name="bom_id" ref="mrp_bom_7"/>
</record>
<record id="mrp_bom_21" model="mrp.bom">
<field name="name">Motherboard A20Z7</field>
<record id="mrp_bom_line_14" model="mrp.bom.line">
<field name="product_id" ref="product.product_product_21"/>
<field name="product_qty">1</field>
<field name="product_uom" ref="product.product_uom_unit"/>
<field name="sequence">4</field>
<field name="type">normal</field>
<field name="bom_id" ref="mrp_bom_19"/>
<field name="bom_id" ref="mrp_bom_7"/>
</record>
<record id="mrp_bom_22" model="mrp.bom">
<field name="name">HDD 500GB</field>
<record id="mrp_bom_line_15" model="mrp.bom.line">
<field name="product_id" ref="product.product_product_18"/>
<field name="product_qty">1</field>
<field name="product_uom" ref="product.product_uom_unit"/>
<field name="sequence">6</field>
<field name="type">normal</field>
<field name="bom_id" ref="mrp_bom_19"/>
<field name="bom_id" ref="mrp_bom_7"/>
</record>
<record id="mrp_bom_23" model="mrp.bom">
<field name="name">Graphics Card</field>
<record id="mrp_bom_line_16" model="mrp.bom.line">
<field name="product_id" ref="product.product_product_24"/>
<field name="product_qty">1</field>
<field name="product_uom" ref="product.product_uom_unit"/>
<field name="sequence">10</field>
<field name="type">normal</field>
<field name="bom_id" ref="mrp_bom_19"/>
<field name="bom_id" ref="mrp_bom_7"/>
</record>
<record id="mrp_bom_24" model="mrp.bom">
<record id="mrp_bom_8" model="mrp.bom">
<field name="name">PC Assemble + 2GB RAM</field>
<field name="product_id" ref="product.product_product_4"/>
<field name="product_qty">1</field>
<field name="product_tmpl_id" ref="product.product_product_3_product_template"/>
<field name="product_uom" ref="product.product_uom_unit"/>
<field name="sequence">5</field>
<field name="type">phantom</field>
</record>
<record id="mrp_bom_25" model="mrp.bom">
<field name="name">15" LCD Monitor </field>
<record id="mrp_bom_line_17" model="mrp.bom.line">
<field name="product_id" ref="product.product_product_6"/>
<field name="product_qty">1</field>
<field name="product_uom" ref="product.product_uom_unit"/>
<field name="sequence">2</field>
<field name="type">normal</field>
<field name="bom_id" ref="mrp_bom_24"/>
<field name="bom_id" ref="mrp_bom_8"/>
</record>
<record id="mrp_bom_26" model="mrp.bom">
<field name="name">Computer Case-1</field>
<record id="mrp_bom_line_18" model="mrp.bom.line">
<field name="product_id" ref="product.product_product_16"/>
<field name="product_qty">1</field>
<field name="product_uom" ref="product.product_uom_unit"/>
<field name="sequence">4</field>
<field name="type">normal</field>
<field name="bom_id" ref="mrp_bom_24"/>
<field name="bom_id" ref="mrp_bom_8"/>
</record>
<record id="mrp_bom_27" model="mrp.bom">
<field name="name">Mouse, Laser</field>
<record id="mrp_bom_line_19" model="mrp.bom.line">
<field name="product_id" ref="product.product_product_11"/>
<field name="product_qty">1</field>
<field name="product_uom" ref="product.product_uom_unit"/>
<field name="sequence">6</field>
<field name="type">normal</field>
<field name="bom_id" ref="mrp_bom_24"/>
<field name="bom_id" ref="mrp_bom_8"/>
</record>
<record id="mrp_bom_28" model="mrp.bom">
<field name="name">USB Keyboard, QWERTY</field>
<record id="mrp_bom_line_20" model="mrp.bom.line">
<field name="product_id" ref="product.product_product_8"/>
<field name="product_qty">1</field>
<field name="product_uom" ref="product.product_uom_unit"/>
<field name="sequence">8</field>
<field name="type">normal</field>
<field name="bom_id" ref="mrp_bom_24"/>
<field name="bom_id" ref="mrp_bom_8"/>
</record>
<record id="mrp_bom_29" model="mrp.bom">
<field name="name">RAM DDR2 1GB</field>
<record id="mrp_bom_line_21" model="mrp.bom.line">
<field name="product_id" ref="product.product_product_15"/>
<field name="product_qty">2</field>
<field name="product_uom" ref="product.product_uom_unit"/>
<field name="sequence">10</field>
<field name="type">normal</field>
<field name="bom_id" ref="mrp_bom_24"/>
<field name="bom_id" ref="mrp_bom_8"/>
</record>
<record id="mrp_bom_30" model="mrp.bom">
<record id="mrp_bom_9" model="mrp.bom">
<field name="name">PC Assemble + 512MB RAM</field>
<field name="product_id" ref="product.product_product_3"/>
<field name="product_qty">1</field>
<field name="product_tmpl_id" ref="product.product_product_3_product_template"/>
<field name="product_uom" ref="product.product_uom_unit"/>
<field name="sequence">5</field>
<field name="routing_id" ref="mrp_routing_2"/>
<field name="type">phantom</field>
</record>
<record id="mrp_bom_31" model="mrp.bom">
<field name="name">17" LCD Monitor</field>
<record id="mrp_bom_line_22" model="mrp.bom.line">
<field name="product_id" ref="product.product_product_7"/>
<field name="product_qty">1</field>
<field name="product_uom" ref="product.product_uom_unit"/>
<field name="sequence">2</field>
<field name="type">normal</field>
<field name="bom_id" ref="mrp_bom_30"/>
<field name="bom_id" ref="mrp_bom_9"/>
</record>
<record id="mrp_bom_32" model="mrp.bom">
<field name="name">Computer Case-2</field>
<record id="mrp_bom_line_23" model="mrp.bom.line">
<field name="product_id" ref="product.product_product_16"/>
<field name="product_qty">1</field>
<field name="product_uom" ref="product.product_uom_unit"/>
<field name="sequence">4</field>
<field name="type">normal</field>
<field name="bom_id" ref="mrp_bom_30"/>
<field name="bom_id" ref="mrp_bom_9"/>
</record>
<record id="mrp_bom_33" model="mrp.bom">
<field name="name">Mouse, Laser</field>
<record id="mrp_bom_line_24" model="mrp.bom.line">
<field name="product_id" ref="product.product_product_11"/>
<field name="product_qty">1</field>
<field name="product_uom" ref="product.product_uom_unit"/>
<field name="sequence">6</field>
<field name="type">normal</field>
<field name="bom_id" ref="mrp_bom_30"/>
<field name="bom_id" ref="mrp_bom_9"/>
</record>
<record id="mrp_bom_34" model="mrp.bom">
<field name="name">USB Keyboard, QWERTY</field>
<record id="mrp_bom_line_25" model="mrp.bom.line">
<field name="product_id" ref="product.product_product_8"/>
<field name="product_qty">1</field>
<field name="product_uom" ref="product.product_uom_unit"/>
<field name="sequence">8</field>
<field name="type">normal</field>
<field name="bom_id" ref="mrp_bom_30"/>
<field name="bom_id" ref="mrp_bom_9"/>
</record>
<record id="mrp_bom_35" model="mrp.bom">
<field name="name">RAM DDR 512MB</field>
<record id="mrp_bom_line_26" model="mrp.bom.line">
<field name="product_id" ref="product.product_product_13"/>
<field name="product_qty">1</field>
<field name="product_uom" ref="product.product_uom_unit"/>
<field name="sequence">10</field>
<field name="type">normal</field>
<field name="bom_id" ref="mrp_bom_30"/>
<field name="bom_id" ref="mrp_bom_9"/>
</record>
<record id="mrp_bom_36" model="mrp.bom">
<record id="mrp_bom_10" model="mrp.bom">
<field name="name">PC Assemble + Custom RAM (PC on Demand)</field>
<field name="product_id" ref="product.product_product_5"/>
<field name="product_qty">1</field>
<field name="product_tmpl_id" ref="product.product_product_5_product_template"/>
<field name="product_uom" ref="product.product_uom_unit"/>
<field name="sequence">5</field>
<field name="routing_id" ref="mrp_routing_2"/>
<field name="type">phantom</field>
</record>
<record id="mrp_bom_37" model="mrp.bom">
<field name="name">15" LCD Monitor </field>
<record id="mrp_bom_line_27" model="mrp.bom.line">
<field name="product_id" ref="product.product_product_6"/>
<field name="product_qty">1</field>
<field name="product_uom" ref="product.product_uom_unit"/>
<field name="sequence">2</field>
<field name="type">normal</field>
<field name="bom_id" ref="mrp_bom_36"/>
<field name="bom_id" ref="mrp_bom_10"/>
</record>
<record id="mrp_bom_38" model="mrp.bom">
<field name="name">Computer Case-1</field>
<record id="mrp_bom_line_28" model="mrp.bom.line">
<field name="product_id" ref="product.product_product_16"/>
<field name="product_qty">1</field>
<field name="product_uom" ref="product.product_uom_unit"/>
<field name="sequence">4</field>
<field name="type">normal</field>
<field name="bom_id" ref="mrp_bom_36"/>
<field name="bom_id" ref="mrp_bom_10"/>
</record>
<record id="mrp_bom_39" model="mrp.bom">
<field name="name">RAM DDR 512MB</field>
<record id="mrp_bom_line_29" model="mrp.bom.line">
<field name="product_id" ref="product.product_product_13"/>
<field name="product_qty">1</field>
<field name="product_uom" ref="product.product_uom_unit"/>
<field name="sequence">6</field>
<field name="type">normal</field>
<field name="bom_id" ref="mrp_bom_36"/>
<field name="bom_id" ref="mrp_bom_10"/>
</record>
<record id="mrp_bom_40" model="mrp.bom">
<field name="name">Mouse, Laser</field>
<record id="mrp_bom_line_30" model="mrp.bom.line">
<field name="product_id" ref="product.product_product_11"/>
<field name="product_qty">1</field>
<field name="product_uom" ref="product.product_uom_unit"/>
<field name="sequence">8</field>
<field name="type">normal</field>
<field name="bom_id" ref="mrp_bom_36"/>
<field name="bom_id" ref="mrp_bom_10"/>
</record>
<record id="mrp_bom_41" model="mrp.bom">
<field name="name">USB Keyboard, QWERTY</field>
<record id="mrp_bom_line_31" model="mrp.bom.line">
<field name="product_id" ref="product.product_product_8"/>
<field name="product_qty">1</field>
<field name="product_uom" ref="product.product_uom_unit"/>
<field name="sequence">10</field>
<field name="type">normal</field>
<field name="bom_id" ref="mrp_bom_36"/>
<field name="bom_id" ref="mrp_bom_10"/>
</record>
<record id="mrp_bom_11" model="mrp.bom">
<field name="name">iPad Retina Display</field>
<field name="product_tmpl_id" ref="product.product_product_4_product_template"/>
<field name="product_uom" ref="product.product_uom_unit"/>
<field name="sequence">1</field>
<field name="type">normal</field>
</record>
<record id="mrp_bom_line_32" model="mrp.bom.line">
<field name="product_id" ref="product.product_product_14"/>
<field name="product_qty">1</field>
<field name="product_uom" ref="product.product_uom_unit"/>
<field name="sequence">1</field>
<field name="type">normal</field>
<field name="attribute_value_ids" eval="[(6,0,[ref('product.product_attribute_value_1')])]"/>
<field name="bom_id" ref="mrp_bom_11"/>
</record>
<record id="mrp_bom_line_33" model="mrp.bom.line">
<field name="product_id" ref="product.product_product_15"/>
<field name="product_qty">1</field>
<field name="product_uom" ref="product.product_uom_unit"/>
<field name="sequence">2</field>
<field name="type">normal</field>
<field name="attribute_value_ids" eval="[(6,0,[ref('product.product_attribute_value_2')])]"/>
<field name="bom_id" ref="mrp_bom_11"/>
</record>
<record id="mrp_bom_line_34" model="mrp.bom.line">
<field name="product_id" ref="product.product_product_23"/>
<field name="product_qty">1</field>
<field name="product_uom" ref="product.product_uom_unit"/>
<field name="sequence">3</field>
<field name="type">normal</field>
<field name="bom_id" ref="mrp_bom_11"/>
</record>
<record id="mrp_production_1" model="mrp.production">
<field name="product_id" ref="product.product_product_4"/>
<field name="product_id" ref="product.product_product_3"/>
<field name="product_uom" ref="product.product_uom_unit"/>
<field name="product_qty">3</field>
<field name="location_src_id" ref="stock.stock_location_stock"/>
<field name="location_dest_id" ref="stock.stock_location_output"/>
<field name="bom_id" ref="mrp_bom_24"/>
<field name="bom_id" ref="mrp_bom_8"/>
<field name="routing_id" ref="mrp.mrp_routing_2"/>
</record>
@ -663,7 +657,7 @@
<field name="product_uom" ref="product.product_uom_unit"/>
<field name="location_src_id" ref="stock.stock_location_stock"/>
<field name="location_dest_id" ref="stock.stock_location_output"/>
<field name="bom_id" ref="mrp.mrp_bom_7"/>
<field name="bom_id" ref="mrp.mrp_bom_4"/>
<field name="routing_id" ref="mrp.mrp_routing_1"/>
</record>
<workflow action="button_confirm" model="mrp.production" ref="mrp_production_1"/>

View File

@ -347,22 +347,17 @@
<form string="Bill of Material" version="7.0">
<group>
<group>
<field name="product_id" on_change="onchange_product_id(product_id, name, product_qty, context)" class="oe_inline"/>
<field name="product_tmpl_id" on_change="onchange_product_tmpl_id(product_tmpl_id, product_qty, context)"/>
<field name="product_id"/>
<field name="product_uom" class="oe_inline" on_change="onchange_uom(product_tmpl_id, product_uom)" groups="product.group_uom"/>
<label for="product_qty" string="Quantity"/>
<div>
<field name="product_qty" class="oe_inline" on_change="onchange_product_id(product_id, name, product_qty, context)"/>
<field name="product_uom" class="oe_inline" on_change="onchange_uom(product_id, product_uom)" groups="product.group_uom"/>
</div>
<label for="product_uos_qty" groups="product.group_uos"/>
<div groups="product.group_uos" >
<field name="product_uos_qty"
class="oe_inline"/>
<label string="-" attrs="{'invisible':[('product_uos','=',False)]}" class="oe_inline"/>
<field name="product_uos" class="oe_inline"/>
<field name="product_qty" class="oe_inline" on_change="onchange_product_tmpl_id(product_tmpl_id, product_qty, context)"/>
<field name="product_uom" class="oe_inline" on_change="onchange_uom(product_tmpl_id, product_uom)" groups="product.group_uom"/>
</div>
<label for="routing_id" class="oe_inline" groups="mrp.group_mrp_routings"/>
<div groups="mrp.group_mrp_routings">
<field name="routing_id" class="oe_inline"/>
<field name="routing_id" class="oe_inline"/>
</div>
</group>
<group>
@ -378,14 +373,19 @@
</group>
<notebook>
<page string="Components">
<field name="bom_lines" widget="one2many_list">
<field name="bom_line_ids" widget="one2many_list">
<tree string="Components" editable="bottom">
<field name="product_id" on_change="onchange_product_id(product_id, name)"/>
<field name="sequence" widget="handle"/>
<field name="product_id" on_change="onchange_product_id(product_id, product_qty)"/>
<field name="type"/>
<field name="product_qty"/>
<field name="product_rounding"/>
<field name="product_efficiency"/>
<field name="product_uom" on_change="onchange_uom(product_id, product_uom)" groups="product.group_uom"/>
<field name="name" invisible="1"/>
<field name="date_start"/>
<field name="date_stop"/>
<field name="routing_id"/>
<field name="attribute_value_ids" widget="many2many_tags"/>
</tree>
</field>
</page>
@ -393,7 +393,6 @@
<group>
<group>
<field name="position"/>
<field name="bom_id"/>
<field name="sequence"/>
<field name="active"/>
</group>
@ -422,11 +421,11 @@
<field name="arch" type="xml">
<search string="Search Bill Of Material">
<field name="name" string="Bill Of Material" filter_domain="['|',('name','ilike',self),('code','ilike',self)]"/>
<field name="bom_lines" string="Components"/>
<field name="product_id"/>
<field name="bom_line_ids" string="Components"/>
<field name="product_tmpl_id"/>
<field name="company_id" groups="base.group_multi_company"/>
<group expand="0" string="Group By...">
<filter string="Product" icon="terp-accessories-archiver" domain="[]" context="{'group_by':'product_id'}"/>
<filter string="Product" icon="terp-accessories-archiver" domain="[]" context="{'group_by':'product_tmpl_id'}"/>
<filter string='Default Unit of Measure' icon="terp-mrp" domain="[]" context="{'group_by' : 'product_uom'}"/>
<filter string="Routing" icon="terp-stock_align_left_24" domain="[]" context="{'group_by':'routing_id'}"/>
<filter string='Type' icon="terp-stock_symbol-selection" domain="[]" context="{'group_by' : 'type'}"/>
@ -439,13 +438,13 @@
<record id="mrp_bom_tree_view" model="ir.ui.view">
<field name="name">mrp.bom.tree</field>
<field name="model">mrp.bom</field>
<field name="field_parent">child_complete_ids</field>
<!--field name="field_parent">child_complete_ids</field-->
<field name="arch" type="xml">
<tree string="Bill of Materials">
<field name="sequence" invisible="1"/>
<field name="name" invisible="1"/>
<field name="product_tmpl_id"/>
<field name="product_id"/>
<field name="product_qty"/>
<field name="product_uom" groups="product.group_uom"/>
<field name="code"/>
<field name="type"/>
@ -455,32 +454,12 @@
</tree>
</field>
</record>
<record id="mrp_bom_component_tree_view" model="ir.ui.view">
<field name="name">mrp.bom.component.tree</field>
<field name="model">mrp.bom</field>
<field name="field_parent">child_complete_ids</field>
<field name="arch" type="xml">
<tree string="BoM Structure">
<field name="sequence" invisible="1"/>
<field name="name" groups="base.group_no_one"/>
<field name="code"/>
<field name="product_id"/>
<field name="bom_id" groups="product.group_mrp_properties"/>
<field name="product_qty"/>
<field name="product_uom" groups="product.group_uom"/>
<field name="type"/>
<field name="routing_id" groups="mrp.group_mrp_routings"/>
<field name="date_start" groups="product.group_mrp_properties"/>
<field name="date_stop" groups="product.group_mrp_properties"/>
</tree>
</field>
</record>
<record id="mrp_bom_form_action" model="ir.actions.act_window">
<field name="name">Bill of Materials</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">mrp.bom</field>
<field name="view_type">form</field>
<field name="domain">[('bom_id','=',False)]</field>
<field name="search_view_id" ref="view_mrp_bom_filter"/>
<field name="help" type="html">
<p class="oe_view_nocontent_create">
@ -495,13 +474,56 @@
</p>
</field>
</record>
<record id="mrp_bom_component_tree_view" model="ir.ui.view">
<field name="name">mrp.bom.component.tree</field>
<field name="model">mrp.bom.line</field>
<field name="arch" type="xml">
<tree string="Components" editable="top">
<field name="sequence" widget="handle"/>
<field name="bom_id"/>
<field name="product_id" on_change="onchange_product_id(product_id)"/>
<field name="type"/>
<field name="product_qty"/>
<field name="product_rounding"/>
<field name="product_efficiency"/>
<field name="product_uom" on_change="onchange_uom(product_id, product_uom)" groups="product.group_uom"/>
<field name="product_uos_qty" groups="product.group_uos"/>
<field name="product_uos" groups="product.group_uos"/>
<field name="date_start"/>
<field name="date_stop"/>
<field name="routing_id"/>
<field name="attribute_value_ids" widget="many2many_tags"/>
<field name="property_ids" widget="many2many_tags"/>
</tree>
</field>
</record>
<record id="view_mrp_bom_line_filter" model="ir.ui.view">
<field name="name">mrp.bom.line.select</field>
<field name="model">mrp.bom.line</field>
<field name="arch" type="xml">
<search string="Search Bill Of Material Components">
<field name="bom_id"/>
<field name="product_id"/>
<group expand="0" string="Group By...">
<filter string="Bill Of Material" icon="terp-accessories-archiver" domain="[]" context="{'group_by':'bom_id'}"/>
<filter string="Product" icon="terp-accessories-archiver" domain="[]" context="{'group_by':'product_id'}"/>
<filter string='Default Unit of Measure' icon="terp-mrp" domain="[]" context="{'group_by' : 'product_uom'}"/>
<filter string="Routing" icon="terp-stock_align_left_24" domain="[]" context="{'group_by':'routing_id'}"/>
<filter string='Type' icon="terp-stock_symbol-selection" domain="[]" context="{'group_by' : 'type'}"/>
<filter string=" Valid From Month" icon="terp-go-month" domain="[]" context="{'group_by':'date_start'}" help="Valid From Date by Month"/>
</group>
</search>
</field>
</record>
<record id="mrp_bom_form_action2" model="ir.actions.act_window">
<field name="name">Bill of Material Components</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">mrp.bom</field>
<field name="view_type">form</field>
<field name="view_id" ref="mrp_bom_component_tree_view"/>
<field name="domain">[]</field>
<field name="res_model">mrp.bom.line</field>
<field name="view_type">tree</field>
<field name="view_mode">tree</field>
<field name="help" type="html">
<p class="oe_view_nocontent_create">
Click to add a component to a bill of material.
@ -512,47 +534,32 @@
</p>
</field>
</record>
<record id="product_search_form_view_procurment" model="ir.ui.view">
<field name="name">product.search.bom</field>
<field name="model">product.product</field>
<field name="inherit_id" ref="product.product_search_form_view"/>
<record id="product_template_search_view_procurment" model="ir.ui.view">
<field name="name">product.template.search.bom</field>
<field name="model">product.template</field>
<field name="inherit_id" ref="product.product_template_search_view"/>
<field name="arch" type="xml">
<xpath expr="//filter[@string='Consumable']" position="after">
<separator/>
<filter string="Components" name="components" icon="terp-accessories-archiver" domain="[('bom_ids','not in',[]),('bom_ids.bom_id','!=',False)]"/>
<filter string="Components" name="components" icon="terp-accessories-archiver" domain="[('bom_ids','not in',[])]"/>
</xpath>
</field>
</record>
<record id="product_template_form_view_inherit" model="ir.ui.view">
<field name="name">product.template.form.view.inherited</field>
<field name="name">product.product.form.view.inherited</field>
<field name="model">product.product</field>
<field name="inherit_id" ref="product.product_template_form_view"/>
<field name="inherit_id" ref="product.product_normal_form_view"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='warranty']" position="before">
<label for="produce_delay"/>
<div>
<group name="sale_condition" position="inside">
<label for="produce_delay" attrs="{'invisible':[('type','=','service')]}"/>
<div attrs="{'invisible':[('type','=','service')]}">
<field name="produce_delay" class="oe_inline"/> days
</div>
</xpath>
</group>
</field>
</record>
<record id="view_normal_procurement_locations_form_inherited" model="ir.ui.view">
<field name="name">product.normal.procurement.locations.inherited</field>
<field name="model">product.product</field>
<field name="inherit_id" ref="product.product_normal_form_view"/>
<field name="arch" type="xml">
<xpath expr="//group[@name='procurement_uom']" position="after">
<group name="delay" string="Delays" attrs="{'invisible':[('type','=','service')]}">
<label for="produce_delay" />
<div attrs="{'invisible':[('type','=','service')]}">
<field name="produce_delay" class="oe_inline" style="vertical-align:baseline"/> days
</div>
</group>
</xpath>
</field>
</record>
<record id="view_mrp_product_form_inherited" model="ir.ui.view">
<field name="name">product.form.mrp.inherited</field>
@ -686,7 +693,7 @@
</div>
<group>
<group>
<field name="product_id" on_change="product_id_change(product_id, product_qty)" domain="[('bom_ids','!=',False),('bom_ids.bom_id','=',False),('bom_ids.type','!=','phantom')]" class="oe_inline" context='{"default_type": "product"}'/>
<field name="product_id" on_change="product_id_change(product_id, product_qty)" domain="[('bom_ids','!=',False),('bom_ids.type','!=','phantom')]" class="oe_inline" context='{"default_type": "product"}'/>
<label for="product_qty"/>
<div>
<field name="product_qty" class="oe_inline" on_change="product_id_change(product_id, product_qty)"/>
@ -980,7 +987,7 @@
<field name="arch" type="xml">
<data>
<xpath expr="//field[@name='origin']" position="before">
<field name="bom_id" domain="[('product_id','=',product_id),('bom_id','=',False)]"/>
<field name="bom_id" domain="[('product_id','=',product_id)]"/>
<field name="production_id" attrs="{'invisible': [('production_id','=',False)]}"/>
</xpath>
<xpath expr="//field[@name='origin']" position="after">
@ -992,7 +999,6 @@
</field>
</record>
<!-- Menu for Resource for MRP-->
<record id="mrp_workcenter_action" model="ir.actions.act_window">
@ -1027,16 +1033,9 @@
id="menu_mrp_production_order_action" name="Order Planning"
parent="menu_mrp_planning" sequence="1"/>
<record id="act_product_mrp_bom_open" model="ir.actions.act_window">
<field name="name">BoM Structure</field>
<field name="context">{'default_product_id': active_id}</field>
<field name="domain">[('product_id', 'in', active_ids),('bom_id','=',False)]</field>
<field name="res_model">mrp.bom</field>
</record>
<record model="ir.actions.act_window" id="product_open_bom">
<field name="context">{'default_product_id': active_id, 'search_default_product_id': active_id}</field>
<field name="context">{'default_product_tmpl_id': active_id, 'search_default_product_tmpl_id': active_id}</field>
<field name="name">Bill of Materials</field>
<field name="domain">[('bom_id','=',False)]</field>
<field name="res_model">mrp.bom</field>
<field name="view_type">form</field>
</record>
@ -1046,25 +1045,21 @@
<field name="res_model">mrp.production</field>
<field name="view_id" ref="mrp_production_tree_view"/>
</record>
<record model="ir.ui.view" id="product_form_view_bom_button">
<field name="name">product.product.procurement</field>
<field name="model">product.product</field>
<field name="inherit_id" ref="product.product_normal_form_view"/>
<record model="ir.ui.view" id="product_template_form_view_bom_button">
<field name="name">product.template.procurement</field>
<field name="model">product.template</field>
<field name="inherit_id" ref="product.product_template_form_view"/>
<field name="arch" type="xml">
<xpath expr="//div[@name='buttons']" position="inside">
<div name="buttons" position="inside">
<button class="oe_inline oe_stat_button" name="%(product_open_bom)d" type="action"
groups="mrp.group_mrp_user" attrs="{'invisible':[('type', '=', 'service')]}" icon="fa-flask">
<field string="Bill of Materials" name="bom_count" widget="statinfo" />
</button>
<button class="oe_inline oe_stat_button" name="%(act_product_mrp_bom_open)d" type="action"
groups="mrp.group_mrp_user" attrs="{'invisible':[('type', '=', 'service')]}" icon="fa-flask">
<field string="BoM Structure" name="bom_strct" widget="statinfo" />
</button>
<button class="oe_inline oe_stat_button" name="%(act_product_mrp_production)d" type="action"
groups="mrp.group_mrp_user" attrs="{'invisible':[('type', '=', 'service')]}" icon="fa-list-alt">
<field string="Manufacturing" name="mo_count" widget="statinfo" />
</button>
</xpath>
</div>
</field>
</record>

View File

@ -62,7 +62,8 @@ class procurement_order(osv.osv):
"""
for procurement in self.browse(cr, uid, ids, context=context):
properties = [x.id for x in procurement.property_ids]
bom_id = self.pool.get('mrp.bom')._bom_find(cr, uid, procurement.product_id.id, procurement.product_uom.id, properties)
bom_id = self.pool.get('mrp.bom')._bom_find(cr, uid, procurement.product_uom.id,
product_id=procurement.product_id.id, properties=properties)
if not bom_id:
return False
return True
@ -83,7 +84,8 @@ class procurement_order(osv.osv):
routing_id = procurement.bom_id.routing_id.id
else:
properties = [x.id for x in procurement.property_ids]
bom_id = bom_obj._bom_find(cr, uid, procurement.product_id.id, procurement.product_uom.id, properties)
bom_id = bom_obj._bom_find(cr, uid, procurement.product_uom.id,
product_id=procurement.product_id.id, properties=properties)
bom = bom_obj.browse(cr, uid, bom_id, context=context)
routing_id = bom.routing_id.id

View File

@ -20,19 +20,30 @@
##############################################################################
from openerp.osv import fields, osv
from openerp.tools.translate import _
class product_product(osv.osv):
_inherit = "product.product"
class product_template(osv.osv):
_inherit = "product.template"
def _bom_orders_count(self, cr, uid, ids, field_name, arg, context=None):
Bom = self.pool('mrp.bom')
res = {}
for product_tmpl_id in ids:
nb = Bom.search_count(cr, uid, [('product_tmpl_id', '=', product_tmpl_id)], context=context)
res[product_tmpl_id] = {
'bom_count': nb,
}
return res
def _bom_orders_count_mo(self, cr, uid, ids, name, arg, context=None):
res = {}
for product_tmpl_id in self.browse(cr, uid, ids):
res[product_tmpl_id.id] = sum([p.mo_count for p in product_tmpl_id.product_variant_ids])
return res
_columns = {
"bom_ids": fields.one2many('mrp.bom', 'product_id','Bill of Materials', domain=[('bom_id','=',False)]),
"produce_delay": fields.float('Manufacturing Lead Time', help="Average delay in days to produce this product. In the case of multi-level BOM, the manufacturing lead times of the components will be added."),
'track_production': fields.boolean('Track Manufacturing Lots', help="Forces to specify a Serial Number for all moves containing this product and generated by a Manufacturing Order"),
}
_defaults = {
"produce_delay": 1,
"bom_ids": fields.one2many('mrp.bom', 'product_tmpl_id','Bill of Materials'),
'bom_count': fields.function(_bom_orders_count, string='# Bill of Material', type='integer', multi="_bom_order_count"),
'mo_count': fields.function(_bom_orders_count_mo, string='# Manufacturing Orders', type='integer'),
}
def copy(self, cr, uid, id, default=None, context=None):
if not default:
@ -40,7 +51,26 @@ class product_product(osv.osv):
default.update({
'bom_ids': []
})
return super(product_product, self).copy(cr, uid, id, default, context=context)
return super(product_template, self).copy(cr, uid, id, default, context=context)
class product_product(osv.osv):
_inherit = "product.product"
def _bom_orders_count(self, cr, uid, ids, field_name, arg, context=None):
Production = self.pool('mrp.production')
res = {}
for product_id in ids:
res[product_id] = Production.search_count(cr,uid, [('product_id', '=', product_id)], context=context)
return res
_columns = {
"produce_delay": fields.float('Manufacturing Lead Time', help="Average delay in days to produce this product. In the case of multi-level BOM, the manufacturing lead times of the components will be added."),
'track_production': fields.boolean('Track Manufacturing Lots', help="Forces to specify a Serial Number for all moves containing this product and generated by a Manufacturing Order"),
'mo_count': fields.function(_bom_orders_count, string='# Manufacturing Orders', type='integer'),
}
_defaults = {
"produce_delay": 1,
}

View File

@ -144,7 +144,7 @@ class report_custom(report_rml):
for product in product_pool.browse(cr, uid, ids, context=context):
product_uom_name = to_xml(product.uom_id.name)
bom_id = bom_pool._bom_find(cr, uid, product.id, product.uom_id.id)
bom_id = bom_pool._bom_find(cr, uid, product.uom_id.id, product_id=product.id)
title = "<title>%s</title>" %(_("Cost Structure"))
title += "<title>%s</title>" % (to_xml(product.name))
xml += "<lines style='header'>" + title + prod_header + "</lines>"
@ -168,7 +168,7 @@ class report_custom(report_rml):
else:
bom = bom_pool.browse(cr, uid, bom_id, context=context)
factor = number * product.uom_id.factor / bom.product_uom.factor
sub_boms = bom_pool._bom_explode(cr, uid, bom, factor / bom.product_qty)
sub_boms = bom_pool._bom_explode(cr, uid, bom, product, factor / bom.product_qty)
total = 0
total_strd = 0
parent_bom = {

View File

@ -4,6 +4,7 @@ access_mrp_workcenter,mrp.workcenter,model_mrp_workcenter,mrp.group_mrp_user,1,0
access_mrp_routing,mrp.routing,model_mrp_routing,mrp.group_mrp_user,1,0,0,0
access_mrp_routing_workcenter,mrp.routing.workcenter,model_mrp_routing_workcenter,mrp.group_mrp_user,1,0,0,0
access_mrp_bom,mrp.bom,model_mrp_bom,group_mrp_user,1,0,0,0
access_mrp_bom_line,mrp.bom.line,model_mrp_bom_line,group_mrp_user,1,0,0,0
access_mrp_production,mrp.production user,model_mrp_production,mrp.group_mrp_user,1,1,1,1
access_mrp_production_salesman,mrp.production salesman,model_mrp_production,base.group_sale_salesman,1,1,1,0
access_mrp_production_product_line_salesman,mrp.production.product.line salesman,model_mrp_production_product_line,base.group_sale_salesman,1,0,1,0
@ -14,6 +15,7 @@ access_mrp_workcenter_manager,mrp.workcenter.manager,model_mrp_workcenter,mrp.gr
access_mrp_routing_manager,mrp.routing.manager,model_mrp_routing,mrp.group_mrp_manager,1,1,1,1
access_mrp_routing_workcenter_manager,mrp.routing.workcenter.manager,model_mrp_routing_workcenter,mrp.group_mrp_manager,1,1,1,1
access_mrp_bom_manager,mrp.bom.manager,model_mrp_bom,mrp.group_mrp_manager,1,1,1,1
access_mrp_bom_line_manager,mrp.bom.line.manager,model_mrp_bom_line,mrp.group_mrp_manager,1,1,1,1
access_stock_location_mrp_worker,stock.location mrp_worker,stock.model_stock_location,mrp.group_mrp_user,1,0,0,0
access_stock_move_mrp_worker,stock.move mrp_worker,stock.model_stock_move,mrp.group_mrp_user,1,1,1,0
access_stock_picking_mrp_worker,stock.picking mrp_worker,stock.model_stock_picking,mrp.group_mrp_user,1,1,1,1
@ -55,6 +57,8 @@ access_report_mrp_inout_user,report.mrp.inout user,model_report_mrp_inout,mrp.gr
access_report_workcenter_load_user,report.workcenter.load.user,model_report_workcenter_load,mrp.group_mrp_user,1,0,0,0
access_mrp_bom_salesman,mrp.bom,model_mrp_bom,base.group_sale_salesman,1,0,0,0
access_mrp_bom_stockuser,mrp.bom,model_mrp_bom,stock.group_stock_user,1,0,0,0
access_mrp_bom_line_salesman,mrp.bom.line,model_mrp_bom_line,base.group_sale_salesman,1,0,0,0
access_mrp_bom_line_stockuser,mrp.bom.line,model_mrp_bom_line,stock.group_stock_user,1,0,0,0
access_product_uom_categ_mrp_manager,product.uom.categ mrp_manager,product.model_product_uom_categ,mrp.group_mrp_manager,1,1,1,1
access_product_uom_mrp_manager,product.uom mrp_manager,product.model_product_uom,mrp.group_mrp_manager,1,1,1,1
access_product_ul_mrp_manager,product.ul mrp_manager,product.model_product_ul,mrp.group_mrp_manager,1,1,1,1

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
4 access_mrp_routing mrp.routing model_mrp_routing mrp.group_mrp_user 1 0 0 0
5 access_mrp_routing_workcenter mrp.routing.workcenter model_mrp_routing_workcenter mrp.group_mrp_user 1 0 0 0
6 access_mrp_bom mrp.bom model_mrp_bom group_mrp_user 1 0 0 0
7 access_mrp_bom_line mrp.bom.line model_mrp_bom_line group_mrp_user 1 0 0 0
8 access_mrp_production mrp.production user model_mrp_production mrp.group_mrp_user 1 1 1 1
9 access_mrp_production_salesman mrp.production salesman model_mrp_production base.group_sale_salesman 1 1 1 0
10 access_mrp_production_product_line_salesman mrp.production.product.line salesman model_mrp_production_product_line base.group_sale_salesman 1 0 1 0
15 access_mrp_routing_manager mrp.routing.manager model_mrp_routing mrp.group_mrp_manager 1 1 1 1
16 access_mrp_routing_workcenter_manager mrp.routing.workcenter.manager model_mrp_routing_workcenter mrp.group_mrp_manager 1 1 1 1
17 access_mrp_bom_manager mrp.bom.manager model_mrp_bom mrp.group_mrp_manager 1 1 1 1
18 access_mrp_bom_line_manager mrp.bom.line.manager model_mrp_bom_line mrp.group_mrp_manager 1 1 1 1
19 access_stock_location_mrp_worker stock.location mrp_worker stock.model_stock_location mrp.group_mrp_user 1 0 0 0
20 access_stock_move_mrp_worker stock.move mrp_worker stock.model_stock_move mrp.group_mrp_user 1 1 1 0
21 access_stock_picking_mrp_worker stock.picking mrp_worker stock.model_stock_picking mrp.group_mrp_user 1 1 1 1
57 access_report_workcenter_load_user report.workcenter.load.user model_report_workcenter_load mrp.group_mrp_user 1 0 0 0
58 access_mrp_bom_salesman mrp.bom model_mrp_bom base.group_sale_salesman 1 0 0 0
59 access_mrp_bom_stockuser mrp.bom model_mrp_bom stock.group_stock_user 1 0 0 0
60 access_mrp_bom_line_salesman mrp.bom.line model_mrp_bom_line base.group_sale_salesman 1 0 0 0
61 access_mrp_bom_line_stockuser mrp.bom.line model_mrp_bom_line stock.group_stock_user 1 0 0 0
62 access_product_uom_categ_mrp_manager product.uom.categ mrp_manager product.model_product_uom_categ mrp.group_mrp_manager 1 1 1 1
63 access_product_uom_mrp_manager product.uom mrp_manager product.model_product_uom mrp.group_mrp_manager 1 1 1 1
64 access_product_ul_mrp_manager product.ul mrp_manager product.model_product_ul mrp.group_mrp_manager 1 1 1 1

View File

@ -55,13 +55,14 @@ class StockMove(osv.osv):
user_company = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.id
#doing the search as SUPERUSER because a user with the permission to write on a stock move should be able to explode it
#without giving him the right to read the boms.
return self.pool.get('mrp.bom').search(cr, SUPERUSER_ID, [
('product_id', '=', move.product_id.id),
('bom_id', '=', False),
domain = [
'|', ('product_id', '=', move.product_id.id),
'&', ('product_id', '=', False), ('product_tmpl_id.product_variant_ids', '=', move.product_id.id),
('type', '=', 'phantom'),
'|', ('date_start', '=', False), ('date_start', '<=', time.strftime(DEFAULT_SERVER_DATETIME_FORMAT)),
'|', ('date_stop', '=', False), ('date_stop', '>=', time.strftime(DEFAULT_SERVER_DATETIME_FORMAT)),
('company_id', '=', user_company)], context=context)
('company_id', '=', user_company)]
return self.pool.get('mrp.bom').search(cr, SUPERUSER_ID, domain, context=context)
def _action_explode(self, cr, uid, move, context=None):
""" Explodes pickings.
@ -76,7 +77,7 @@ class StockMove(osv.osv):
if bis:
factor = move.product_qty
bom_point = bom_obj.browse(cr, SUPERUSER_ID, bis[0], context=context)
res = bom_obj._bom_explode(cr, SUPERUSER_ID, bom_point, factor, [])
res = bom_obj._bom_explode(cr, SUPERUSER_ID, bom_point, move.product_id, factor, [])
state = 'confirmed'
if move.state == 'assigned':
state = 'assigned'

View File

@ -4,17 +4,17 @@
!record {model: mrp.bom, id: mrp_bom_test1}:
company_id: base.main_company
name: PC Assemble SC234
product_tmpl_id: product.product_product_3_product_template
product_id: product.product_product_3
product_uom: product.product_uom_unit
product_qty: 1.0
type: normal
bom_lines:
- company_id: base.main_company
name: On Site Assistance
product_id: product.product_product_2
bom_line_ids:
- product_id: product.product_product_2
product_uom: product.product_uom_unit
product_qty: 1.0
- company_id: base.main_company
name: GrapWorks Software
product_id: product.product_product_44
- product_id: product.product_product_44
product_uom: product.product_uom_unit
product_qty: 1.0
-
I make the production order using BoM having one service type product and one consumable product.

View File

@ -7,11 +7,11 @@
I create Production Order of PC Assemble SC349 to produce 5.0 Unit.
-
!record {model: mrp.production, id: mrp_production_test1}:
product_id: product.product_product_4
product_id: product.product_product_3
product_qty: 5.0
location_src_id: stock.stock_location_14
location_dest_id: stock.stock_location_output
bom_id: mrp_bom_24
bom_id: mrp_bom_9
routing_id: mrp.mrp_routing_1

View File

@ -27,10 +27,9 @@
</td>
<td><span t-field="o.code"/></td>
</tr>
<tr t-foreach="get_children(o.bom_lines)" t-as="l">
<tr t-foreach="get_children(o.bom_line_ids)" t-as="l">
<td>
<span style="color: white;" t-esc="'... '*(l['level'])"/>
- <span t-esc="l['name']"/>
</td>
<td>
[ <span t-esc="l['pcode']"/> ]

View File

@ -78,7 +78,7 @@ class change_production_qty(osv.osv_memory):
bom_point = prod.bom_id
bom_id = prod.bom_id.id
if not bom_point:
bom_id = bom_obj._bom_find(cr, uid, prod.product_id.id, prod.product_uom.id)
bom_id = bom_obj._bom_find(cr, uid, prod.product_uom.id, product_id=prod.product_id.id)
if not bom_id:
raise osv.except_osv(_('Error!'), _("Cannot find bill of material for this product."))
prod_obj.write(cr, uid, [prod.id], {'bom_id': bom_id})
@ -89,7 +89,7 @@ class change_production_qty(osv.osv_memory):
factor = prod.product_qty * prod.product_uom.factor / bom_point.product_uom.factor
product_details, workcenter_details = \
bom_obj._bom_explode(cr, uid, bom_point, factor / bom_point.product_qty, [])
bom_obj._bom_explode(cr, uid, bom_point, prod.product_id, factor / bom_point.product_qty, [])
for r in product_details:
if r['product_id'] == move.product_id.id:
move_obj.write(cr, uid, [move.id], {'product_uom_qty': r['product_qty']})

View File

@ -4,7 +4,7 @@
-
I add a sub product in Bill of material for product External Hard Disk.
-
!record {model: mrp.bom, id: mrp.mrp_bom_24}:
!record {model: mrp.bom, id: mrp.mrp_bom_9}:
product_id: product.product_product_28
name: External Hard Disk + Subproduct
product_uom: product.product_uom_unit
@ -20,7 +20,7 @@
product_id: product.product_product_28
product_qty: 2.0
product_uom: product.product_uom_unit
bom_id: mrp.mrp_bom_24
bom_id: mrp.mrp_bom_9
location_src_id: stock.stock_location_stock
-
I compute the data of production order.

View File

@ -22,7 +22,7 @@
<p t-if="o.partner_id.vat">VAT: <span t-field="o.partner_id.vat"/></p>
</div>
</div>
<div class="col-xs-4 col-xs-offset-2">
<div class="col-xs-5 col-xs-offset-1">
<div t-field="o.partner_id"
t-field-options='{"widget": "contact", "fields": ["address", "name"], "no_marker": true}' />
</div>

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

View File

@ -16,4 +16,7 @@ class AccountPaymentConfig(osv.TransientModel):
'module_payment_adyen': fields.boolean(
'Manage Payments Using Adyen',
help='-It installs the module payment_adyen.'),
'module_payment_buckaroo': fields.boolean(
'Manage Payments Using Buckaroo',
help='-It installs the module payment_buckaroo.'),
}

View File

@ -20,6 +20,10 @@
<field name="module_payment_adyen" class="oe_inline"/>
<label for="module_payment_adyen"/>
</div>
<div>
<field name="module_payment_buckaroo" class="oe_inline"/>
<label for="module_payment_buckaroo"/>
</div>
</xpath>
</field>
</record>

View File

@ -1,7 +1,8 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
# Copyright (C) 2014-Today OpenERP SA (<http://www.openerp.com>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
@ -18,7 +19,5 @@
#
##############################################################################
import product_manufacturer
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
import models
import controllers

View File

@ -0,0 +1,17 @@
# -*- coding: utf-8 -*-
{
'name': 'Buckaroo Payment Acquirer',
'category': 'Hidden',
'summary': 'Payment Acquirer: Buckaroo Implementation',
'version': '1.0',
'description': """Buckaroo Payment Acquirer""",
'author': 'OpenERP SA',
'depends': ['payment'],
'data': [
'views/buckaroo.xml',
'views/payment_acquirer.xml',
'data/buckaroo.xml',
],
'installable': True,
}

View File

@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-
import main

View File

@ -0,0 +1,38 @@
# -*- coding: utf-8 -*-
try:
import simplejson as json
except ImportError:
import json
import logging
import pprint
import werkzeug
from openerp import http, SUPERUSER_ID
from openerp.http import request
_logger = logging.getLogger(__name__)
class BuckarooController(http.Controller):
_return_url = '/payment/buckaroo/return'
_cancel_url = '/payment/buckaroo/cancel'
_exception_url = '/payment/buckaroo/error'
_reject_url = '/payment/buckaroo/reject'
@http.route([
'/payment/buckaroo/return',
'/payment/buckaroo/cancel',
'/payment/buckaroo/error',
'/payment/buckaroo/reject',
], type='http', auth='none')
def buckaroo_return(self, **post):
""" Buckaroo."""
_logger.info('Buckaroo: entering form_feedback with post data %s', pprint.pformat(post)) # debug
request.registry['payment.transaction'].form_feedback(request.cr, SUPERUSER_ID, post, 'buckaroo', context=request.context)
return_url = post.pop('return_url', '')
if not return_url:
data ='' + post.pop('ADD_RETURNDATA', '{}').replace("'", "\"")
custom = json.loads(data)
return_url = custom.pop('return_url', '/')
return werkzeug.utils.redirect(return_url)

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data noupdate="1">
<record id="payment_acquirer_buckaroo" model="payment.acquirer">
<field name="name">Buckaroo</field>
<field name="provider">buckaroo</field>
<field name="company_id" ref="base.main_company"/>
<field name="view_template_id" ref="buckaroo_acquirer_button"/>
<field name="environment">test</field>
<field name="pre_msg"><![CDATA[
<p>You will be redirected to the Buckaroo website after cliking on the payment button.</p>]]></field>
<field name="brq_websitekey">dummy</field>
<field name="brq_secretkey">dummy</field>
</record>
</data>
</openerp>

View File

@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-
import buckaroo

View File

@ -0,0 +1,191 @@
# -*- coding: utf-'8' "-*-"
from hashlib import sha1
import logging
import urlparse
from openerp.addons.payment.models.payment_acquirer import ValidationError
from openerp.addons.payment_buckaroo.controllers.main import BuckarooController
from openerp.osv import osv, fields
from openerp.tools.float_utils import float_compare
_logger = logging.getLogger(__name__)
class AcquirerBuckaroo(osv.Model):
_inherit = 'payment.acquirer'
def _get_buckaroo_urls(self, cr, uid, environment, context=None):
""" Buckaroo URLs
"""
if environment == 'prod':
return {
'buckaroo_form_url': 'https://checkout.buckaroo.nl/html/',
}
else:
return {
'buckaroo_form_url': 'https://testcheckout.buckaroo.nl/html/',
}
def _get_providers(self, cr, uid, context=None):
providers = super(AcquirerBuckaroo, self)._get_providers(cr, uid, context=context)
providers.append(['buckaroo', 'Buckaroo'])
return providers
_columns = {
'brq_websitekey': fields.char('WebsiteKey', required_if_provider='buckaroo'),
'brq_secretkey': fields.char('SecretKey', required_if_provider='buckaroo'),
}
def _buckaroo_generate_digital_sign(self, acquirer, inout, values):
""" Generate the shasign for incoming or outgoing communications.
:param browse acquirer: the payment.acquirer browse record. It should
have a shakey in shaky out
:param string inout: 'in' (openerp contacting buckaroo) or 'out' (buckaroo
contacting openerp).
:param dict values: transaction values
:return string: shasign
"""
assert inout in ('in', 'out')
assert acquirer.provider == 'buckaroo'
keys = "add_returndata Brq_amount Brq_culture Brq_currency Brq_invoicenumber Brq_return Brq_returncancel Brq_returnerror Brq_returnreject brq_test Brq_websitekey".split()
def get_value(key):
if values.get(key):
return values[key]
return ''
if inout == 'out':
if 'BRQ_SIGNATURE' in values:
del values['BRQ_SIGNATURE']
items = sorted((k.upper(), v) for k, v in values.items())
sign = ''.join('%s=%s' % (k, v) for k, v in items)
else:
sign = ''.join('%s=%s' % (k,get_value(k)) for k in keys)
#Add the pre-shared secret key at the end of the signature
sign = sign + acquirer.brq_secretkey
if isinstance(sign, str):
sign = urlparse.parse_qsl(sign)
shasign = sha1(sign).hexdigest()
return shasign
def buckaroo_form_generate_values(self, cr, uid, id, partner_values, tx_values, context=None):
base_url = self.pool['ir.config_parameter'].get_param(cr, uid, 'web.base.url')
acquirer = self.browse(cr, uid, id, context=context)
buckaroo_tx_values = dict(tx_values)
buckaroo_tx_values.update({
'Brq_websitekey': acquirer.brq_websitekey,
'Brq_amount': tx_values['amount'],
'Brq_currency': tx_values['currency'] and tx_values['currency'].name or '',
'Brq_invoicenumber': tx_values['reference'],
'brq_test' : True,
'Brq_return': '%s' % urlparse.urljoin(base_url, BuckarooController._return_url),
'Brq_returncancel': '%s' % urlparse.urljoin(base_url, BuckarooController._cancel_url),
'Brq_returnerror': '%s' % urlparse.urljoin(base_url, BuckarooController._exception_url),
'Brq_returnreject': '%s' % urlparse.urljoin(base_url, BuckarooController._reject_url),
'Brq_culture': 'en-US',
})
if buckaroo_tx_values.get('return_url'):
buckaroo_tx_values['add_returndata'] = {'return_url': '%s' % buckaroo_tx_values.pop('return_url')}
else:
buckaroo_tx_values['add_returndata'] = ''
buckaroo_tx_values['Brq_signature'] = self._buckaroo_generate_digital_sign(acquirer, 'in', buckaroo_tx_values)
return partner_values, buckaroo_tx_values
def buckaroo_get_form_action_url(self, cr, uid, id, context=None):
acquirer = self.browse(cr, uid, id, context=context)
return self._get_buckaroo_urls(cr, uid, acquirer.environment, context=context)['buckaroo_form_url']
class TxBuckaroo(osv.Model):
_inherit = 'payment.transaction'
# buckaroo status
_buckaroo_valid_tx_status = [190]
_buckaroo_pending_tx_status = [790, 791, 792, 793]
_buckaroo_cancel_tx_status = [890, 891]
_buckaroo_error_tx_status = [490, 491, 492]
_buckaroo_reject_tx_status = [690]
_columns = {
'buckaroo_txnid': fields.char('Transaction ID'),
}
# --------------------------------------------------
# FORM RELATED METHODS
# --------------------------------------------------
def _buckaroo_form_get_tx_from_data(self, cr, uid, data, context=None):
""" Given a data dict coming from buckaroo, verify it and find the related
transaction record. """
reference, pay_id, shasign = data.get('BRQ_INVOICENUMBER'), data.get('BRQ_PAYMENT'), data.get('BRQ_SIGNATURE')
if not reference or not pay_id or not shasign:
error_msg = 'Buckaroo: received data with missing reference (%s) or pay_id (%s) or shashign (%s)' % (reference, pay_id, shasign)
_logger.error(error_msg)
raise ValidationError(error_msg)
tx_ids = self.search(cr, uid, [('reference', '=', reference)], context=context)
if not tx_ids or len(tx_ids) > 1:
error_msg = 'Buckaroo: received data for reference %s' % (reference)
if not tx_ids:
error_msg += '; no order found'
else:
error_msg += '; multiple order found'
_logger.error(error_msg)
raise ValidationError(error_msg)
tx = self.pool['payment.transaction'].browse(cr, uid, tx_ids[0], context=context)
#verify shasign
shasign_check = self.pool['payment.acquirer']._buckaroo_generate_digital_sign(tx.acquirer_id, 'out' ,data)
if shasign_check.upper() != shasign.upper():
error_msg = 'Buckaroo: invalid shasign, received %s, computed %s, for data %s' % (shasign, shasign_check, data)
_logger.error(error_msg)
raise ValidationError(error_msg)
return tx
def _buckaroo_form_get_invalid_parameters(self, cr, uid, tx, data, context=None):
invalid_parameters = []
if tx.acquirer_reference and data.get('BRQ_TRANSACTIONS') != tx.acquirer_reference:
invalid_parameters.append(('Transaction Id', data.get('BRQ_TRANSACTIONS'), tx.acquirer_reference))
# check what is buyed
if float_compare(float(data.get('BRQ_AMOUNT', '0.0')), tx.amount, 2) != 0:
invalid_parameters.append(('Amount', data.get('BRQ_AMOUNT'), '%.2f' % tx.amount))
if data.get('BRQ_CURRENCY') != tx.currency_id.name:
invalid_parameters.append(('Currency', data.get('BRQ_CURRENCY'), tx.currency_id.name))
return invalid_parameters
def _buckaroo_form_validate(self, cr, uid, tx, data, context=None):
status_code = int(data.get('BRQ_STATUSCODE','0'))
if status_code in self._buckaroo_valid_tx_status:
tx.write({
'state': 'done',
'buckaroo_txnid': data.get('BRQ_TRANSACTIONS'),
})
return True
elif status_code in self._buckaroo_pending_tx_status:
tx.write({
'state': 'pending',
'buckaroo_txnid': data.get('BRQ_TRANSACTIONS'),
})
return True
elif status_code in self._buckaroo_cancel_tx_status:
tx.write({
'state': 'cancel',
'buckaroo_txnid': data.get('BRQ_TRANSACTIONS'),
})
return True
else:
error = 'Buckaroo: feedback error'
_logger.info(error)
tx.write({
'state': 'error',
'state_message': error,
'buckaroo_txnid': data.get('BRQ_TRANSACTIONS'),
})
return False

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

View File

@ -0,0 +1,7 @@
# -*- coding: utf-8 -*-
from openerp.addons.payment_buckaroo.tests import test_buckaroo
checks = [
test_buckaroo,
]

View File

@ -0,0 +1,178 @@
# -*- coding: utf-8 -*-
from lxml import objectify
import urlparse
import openerp
from openerp.addons.payment.models.payment_acquirer import ValidationError
from openerp.addons.payment.tests.common import PaymentAcquirerCommon
from openerp.addons.payment_buckaroo.controllers.main import BuckarooController
from openerp.tools import mute_logger
@openerp.tests.common.at_install(False)
@openerp.tests.common.post_install(False)
class BuckarooCommon(PaymentAcquirerCommon):
def setUp(self):
super(BuckarooCommon, self).setUp()
cr, uid = self.cr, self.uid
self.base_url = self.registry('ir.config_parameter').get_param(cr, uid, 'web.base.url')
# get the buckaroo account
model, self.buckaroo_id = self.registry('ir.model.data').get_object_reference(cr, uid, 'payment_buckaroo', 'payment_acquirer_buckaroo')
@openerp.tests.common.at_install(False)
@openerp.tests.common.post_install(False)
class BuckarooForm(BuckarooCommon):
def test_10_Buckaroo_form_render(self):
cr, uid, context = self.cr, self.uid, {}
# be sure not to do stupid things
buckaroo = self.payment_acquirer.browse(self.cr, self.uid, self.buckaroo_id, None)
self.assertEqual(buckaroo.environment, 'test', 'test without test environment')
# ----------------------------------------
# Test: button direct rendering
# ----------------------------------------
form_values = {
'add_returndata': None,
'Brq_websitekey': buckaroo.brq_websitekey,
'Brq_amount': '2240.0',
'Brq_currency': 'EUR',
'Brq_invoicenumber': 'SO004',
'Brq_signature': '1b8c10074c622d965272a91a9e88b5b3777d2474', # update me
'brq_test': 'True',
'Brq_return': '%s' % urlparse.urljoin(self.base_url, BuckarooController._return_url),
'Brq_returncancel': '%s' % urlparse.urljoin(self.base_url, BuckarooController._cancel_url),
'Brq_returnerror': '%s' % urlparse.urljoin(self.base_url, BuckarooController._exception_url),
'Brq_returnreject': '%s' % urlparse.urljoin(self.base_url, BuckarooController._reject_url),
'Brq_culture': 'en-US',
}
# render the button
res = self.payment_acquirer.render(
cr, uid, self.buckaroo_id,
'SO004', 2240.0, self.currency_euro_id,
partner_id=None,
partner_values=self.buyer_values,
context=context)
# check form result
tree = objectify.fromstring(res)
self.assertEqual(tree.get('action'), 'https://testcheckout.buckaroo.nl/html/', 'Buckaroo: wrong form POST url')
for form_input in tree.input:
if form_input.get('name') in ['submit']:
continue
self.assertEqual(
form_input.get('value'),
form_values[form_input.get('name')],
'Buckaroo: wrong value for input %s: received %s instead of %s' % (form_input.get('name'), form_input.get('value'), form_values[form_input.get('name')])
)
# ----------------------------------------
# Test2: button using tx + validation
# ----------------------------------------
# create a new draft tx
tx_id = self.payment_transaction.create(
cr, uid, {
'amount': 2240.0,
'acquirer_id': self.buckaroo_id,
'currency_id': self.currency_euro_id,
'reference': 'SO004',
'partner_id': self.buyer_id,
}, context=context
)
# render the button
res = self.payment_acquirer.render(
cr, uid, self.buckaroo_id,
'should_be_erased', 2240.0, self.currency_euro,
tx_id=tx_id,
partner_id=None,
partner_values=self.buyer_values,
context=context)
# check form result
tree = objectify.fromstring(res)
self.assertEqual(tree.get('action'), 'https://testcheckout.buckaroo.nl/html/', 'Buckaroo: wrong form POST url')
for form_input in tree.input:
if form_input.get('name') in ['submit']:
continue
self.assertEqual(
form_input.get('value'),
form_values[form_input.get('name')],
'Buckaroo: wrong value for form input %s: received %s instead of %s' % (form_input.get('name'), form_input.get('value'), form_values[form_input.get('name')])
)
@mute_logger('openerp.addons.payment_buckaroo.models.buckaroo', 'ValidationError')
def test_20_buckaroo_form_management(self):
cr, uid, context = self.cr, self.uid, {}
# be sure not to do stupid thing
buckaroo = self.payment_acquirer.browse(self.cr, self.uid, self.buckaroo_id, None)
self.assertEqual(buckaroo.environment, 'test', 'test without test environment')
# typical data posted by buckaroo after client has successfully paid
buckaroo_post_data = {
'BRQ_RETURNDATA': u'',
'BRQ_AMOUNT': u'2240.00',
'BRQ_CURRENCY': u'EUR',
'BRQ_CUSTOMER_NAME': u'Jan de Tester',
'BRQ_INVOICENUMBER': u'SO004',
'BRQ_PAYMENT': u'573311D081B04069BD6336001611DBD4',
'BRQ_PAYMENT_METHOD': u'paypal',
'BRQ_SERVICE_PAYPAL_PAYERCOUNTRY': u'NL',
'BRQ_SERVICE_PAYPAL_PAYEREMAIL': u'fhe@openerp.com',
'BRQ_SERVICE_PAYPAL_PAYERFIRSTNAME': u'Jan',
'BRQ_SERVICE_PAYPAL_PAYERLASTNAME': u'Tester',
'BRQ_SERVICE_PAYPAL_PAYERMIDDLENAME': u'de',
'BRQ_SERVICE_PAYPAL_PAYERSTATUS': u'verified',
'BRQ_SIGNATURE': u'175d82dd53a02bad393fee32cb1eafa3b6fbbd91',
'BRQ_STATUSCODE': u'190',
'BRQ_STATUSCODE_DETAIL': u'S001',
'BRQ_STATUSMESSAGE': u'Transaction successfully processed',
'BRQ_TEST': u'true',
'BRQ_TIMESTAMP': u'2014-05-08 12:41:21',
'BRQ_TRANSACTIONS': u'D6106678E1D54EEB8093F5B3AC42EA7B',
'BRQ_WEBSITEKEY': u'5xTGyGyPyl',
}
# should raise error about unknown tx
with self.assertRaises(ValidationError):
self.payment_transaction.form_feedback(cr, uid, buckaroo_post_data, 'buckaroo', context=context)
tx_id = self.payment_transaction.create(
cr, uid, {
'amount': 2240.0,
'acquirer_id': self.buckaroo_id,
'currency_id': self.currency_euro_id,
'reference': 'SO004',
'partner_name': 'Norbert Buyer',
'partner_country_id': self.country_france_id,
}, context=context
)
# validate it
self.payment_transaction.form_feedback(cr, uid, buckaroo_post_data, 'buckaroo', context=context)
# check state
tx = self.payment_transaction.browse(cr, uid, tx_id, context=context)
self.assertEqual(tx.state, 'done', 'Buckaroo: validation did not put tx into done state')
self.assertEqual(tx.buckaroo_txnid, buckaroo_post_data.get('BRQ_TRANSACTIONS'), 'Buckaroo: validation did not update tx payid')
# reset tx
tx.write({'state': 'draft', 'date_validate': False, 'buckaroo_txnid': False})
# now buckaroo post is ok: try to modify the SHASIGN
buckaroo_post_data['BRQ_SIGNATURE'] = '54d928810e343acf5fb0c3ee75fd747ff159ef7a'
with self.assertRaises(ValidationError):
self.payment_transaction.form_feedback(cr, uid, buckaroo_post_data, 'buckaroo', context=context)
# simulate an error
buckaroo_post_data['BRQ_STATUSCODE'] = 2
buckaroo_post_data['BRQ_SIGNATURE'] = '4164b52adb1e6a2221d3d8a39d8c3e18a9ecb90b'
self.payment_transaction.form_feedback(cr, uid, buckaroo_post_data, 'buckaroo', context=context)
# check state
tx = self.payment_transaction.browse(cr, uid, tx_id, context=context)
self.assertEqual(tx.state, 'error', 'Buckaroo: erroneous validation did not put tx into error state')

View File

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data noupdate="1">
<template id="buckaroo_acquirer_button">
<form t-if="acquirer.brq_websitekey" t-att-action="tx_url" method="post" target="_self">
<input type="hidden" name="Brq_websitekey" t-att-value="tx_values['Brq_websitekey']"/>
<input type="hidden" name="Brq_amount" t-att-value="tx_values['Brq_amount'] or '0.0'"/>
<input type="hidden" name="Brq_currency" t-att-value="tx_values['Brq_currency']"/>
<input type="hidden" name="Brq_invoicenumber" t-att-value="tx_values['Brq_invoicenumber']"/>
<input type="hidden" name="Brq_signature" t-att-value="tx_values['Brq_signature']"/>
<input type="hidden" name="brq_test" t-att-value="tx_values['brq_test']"/>
<input type="hidden" name="Brq_culture" t-att-value="tx_values['Brq_culture']"/>
<!-- URLs -->
<input t-if="tx_values.get('Brq_return')" type='hidden' name='Brq_return'
t-att-value="tx_values.get('Brq_return')"/>
<input t-if="tx_values.get('Brq_returncancel')" type='hidden' name='Brq_returncancel'
t-att-value="tx_values.get('Brq_returncancel')"/>
<input t-if="tx_values.get('Brq_returnerror')" type='hidden' name='Brq_returnerror'
t-att-value="tx_values.get('Brq_returnerror')"/>
<input t-if="tx_values.get('Brq_returnreject')" type='hidden' name='Brq_returnreject'
t-att-value="tx_values.get('Brq_returnreject')"/>
<input type='hidden' name='add_returndata' t-att-value="tx_values.get('add_returndata')"/>
<!-- submit -->
<button type="submit" width="100px"
t-att-class="submit_class">
<img t-if="not submit_txt" src="/payment_buckaroo/static/src/img/buckaroo_icon.png"/>
<span t-if="submit_txt"><t t-esc="submit_txt"/> <span class="fa fa-long-arrow-right"/></span>
</button>
</form>
</template>
</data>
</openerp>

View File

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="acquirer_form_buckaroo" model="ir.ui.view">
<field name="name">acquirer.form.buckaroo</field>
<field name="model">payment.acquirer</field>
<field name="inherit_id" ref="payment.acquirer_form"/>
<field name="arch" type="xml">
<xpath expr='//group[@name="acquirer_display"]' position='after'>
<group attrs="{'invisible': [('provider', '!=', 'buckaroo')]}">
<field name="brq_websitekey"/>
<field name="brq_secretkey"/>
</group>
</xpath>
</field>
</record>
<record id="transaction_form_buckaroo" model="ir.ui.view">
<field name="name">acquirer.transaction.form.buckaroo</field>
<field name="model">payment.transaction</field>
<field name="inherit_id" ref="payment.transaction_form"/>
<field name="arch" type="xml">
<xpath expr='//notebook' position='inside'>
<page string="Buckaroo TX Details">
<group>
<field name="buckaroo_txnid"/>
</group>
</page>
</xpath>
</field>
</record>
</data>
</openerp>

View File

@ -95,9 +95,12 @@ class AcquirerPaypal(osv.Model):
return 0.0
country = self.pool['res.country'].browse(cr, uid, country_id, context=context)
if country and acquirer.company_id.country_id.id == country.id:
fees = amount * (1 + acquirer.fees_dom_var / 100.0) + acquirer.fees_dom_fixed - amount
percentage = acquirer.fees_dom_var
fixed = acquirer.fees_dom_fixed
else:
fees = amount * (1 + acquirer.fees_int_var / 100.0) + acquirer.fees_int_fixed - amount
percentage = acquirer.fees_int_var
fixed = acquirer.fees_int_fixed
fees = (percentage / 100.0 * amount + fixed ) / (1 - percentage / 100.0)
return fees
def paypal_form_generate_values(self, cr, uid, id, partner_values, tx_values, context=None):

View File

@ -148,7 +148,7 @@ class PaypalForm(PaypalCommon):
for form_input in tree.input:
if form_input.get('name') in ['handling']:
handling_found = True
self.assertEqual(form_input.get('value'), '1.56', 'paypal: wrong computed fees')
self.assertEqual(form_input.get('value'), '1.57', 'paypal: wrong computed fees')
self.assertTrue(handling_found, 'paypal: fees_active did not add handling input in rendered form')
@mute_logger('openerp.addons.payment_paypal.models.paypal', 'ValidationError')

View File

@ -1316,30 +1316,80 @@ class ean_wizard(osv.osv_memory):
self.pool[m].write(cr,uid,[m_id],{'ean13':ean13})
return { 'type' : 'ir.actions.act_window_close' }
class product_product(osv.osv):
_inherit = 'product.product'
class pos_category(osv.osv):
_name = "pos.category"
_description = "Public Category"
_order = "sequence, name"
_constraints = [
(osv.osv._check_recursion, 'Error ! You cannot create recursive categories.', ['parent_id'])
]
#def _get_small_image(self, cr, uid, ids, prop, unknow_none, context=None):
# result = {}
# for obj in self.browse(cr, uid, ids, context=context):
# if not obj.product_image:
# result[obj.id] = False
# continue
def name_get(self, cr, uid, ids, context=None):
if not len(ids):
return []
reads = self.read(cr, uid, ids, ['name','parent_id'], context=context)
res = []
for record in reads:
name = record['name']
if record['parent_id']:
name = record['parent_id'][1]+' / '+name
res.append((record['id'], name))
return res
# image_stream = io.BytesIO(obj.product_image.decode('base64'))
# img = Image.open(image_stream)
# img.thumbnail((120, 100), Image.ANTIALIAS)
# img_stream = StringIO.StringIO()
# img.save(img_stream, "JPEG")
# result[obj.id] = img_stream.getvalue().encode('base64')
# return result
def _name_get_fnc(self, cr, uid, ids, prop, unknow_none, context=None):
res = self.name_get(cr, uid, ids, context=context)
return dict(res)
def _get_image(self, cr, uid, ids, name, args, context=None):
result = dict.fromkeys(ids, False)
for obj in self.browse(cr, uid, ids, context=context):
result[obj.id] = tools.image_get_resized_images(obj.image)
return result
def _set_image(self, cr, uid, id, name, value, args, context=None):
return self.write(cr, uid, [id], {'image': tools.image_resize_image_big(value)}, context=context)
_columns = {
'name': fields.char('Name', required=True, translate=True),
'complete_name': fields.function(_name_get_fnc, type="char", string='Name'),
'parent_id': fields.many2one('pos.category','Parent Category', select=True),
'child_id': fields.one2many('pos.category', 'parent_id', string='Children Categories'),
'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of product categories."),
# NOTE: there is no 'default image', because by default we don't show thumbnails for categories. However if we have a thumbnail
# for at least one category, then we display a default image on the other, so that the buttons have consistent styling.
# In this case, the default image is set by the js code.
# NOTE2: image: all image fields are base64 encoded and PIL-supported
'image': fields.binary("Image",
help="This field holds the image used as image for the cateogry, limited to 1024x1024px."),
'image_medium': fields.function(_get_image, fnct_inv=_set_image,
string="Medium-sized image", type="binary", multi="_get_image",
store={
'pos.category': (lambda self, cr, uid, ids, c={}: ids, ['image'], 10),
},
help="Medium-sized image of the category. It is automatically "\
"resized as a 128x128px image, with aspect ratio preserved. "\
"Use this field in form views or some kanban views."),
'image_small': fields.function(_get_image, fnct_inv=_set_image,
string="Smal-sized image", type="binary", multi="_get_image",
store={
'pos.category': (lambda self, cr, uid, ids, c={}: ids, ['image'], 10),
},
help="Small-sized image of the category. It is automatically "\
"resized as a 64x64px image, with aspect ratio preserved. "\
"Use this field anywhere a small image is required."),
}
class product_template(osv.osv):
_inherit = 'product.template'
_columns = {
'income_pdt': fields.boolean('Point of Sale Cash In', help="Check if, this is a product you can use to put cash into a statement for the point of sale backend."),
'expense_pdt': fields.boolean('Point of Sale Cash Out', help="Check if, this is a product you can use to take cash from a statement for the point of sale backend, example: money lost, transfer to bank, etc."),
'available_in_pos': fields.boolean('Available in the Point of Sale', help='Check if you want this product to appear in the Point of Sale'),
'to_weight' : fields.boolean('To Weigh', help="Check if the product should be weighted (mainly used with self check-out interface)."),
'pos_categ_id': fields.many2one('pos.category','Point of Sale Category', help="Those categories are used to group similar products for point of sale."),
}
_defaults = {

File diff suppressed because it is too large Load Diff

View File

@ -477,13 +477,68 @@
<record id="product_normal_form_view" model="ir.ui.view">
<field name="name">product.normal.form.inherit</field>
<field name="model">product.product</field>
<field name="inherit_id" ref="stock.view_normal_procurement_locations_form"/>
<!-- Product Public Categories -->
<record id="product_pos_category_form_view" model="ir.ui.view">
<field name="name">pos.category.form</field>
<field name="model">pos.category</field>
<field name="arch" type="xml">
<group name="sale" position="inside">
<form string="Pos Categories" version="7.0">
<sheet>
<field name="image_medium" widget='image' class="oe_avatar oe_right"/>
<div class="oe_left">
<group>
<field name="name"/>
<field name="parent_id"/>
<field name="sequence"/>
</group>
</div>
</sheet>
</form>
</field>
</record>
<record id="product_pos_category_tree_view" model="ir.ui.view">
<field name="name">pos.category.tree</field>
<field name="model">pos.category</field>
<field name="field_parent" eval="False"/>
<field name="arch" type="xml">
<tree string="Product Product Categories">
<field name="sequence" invisible="1"/>
<field name="complete_name"/>
</tree>
</field>
</record>
<record id="product_pos_category_action" model="ir.actions.act_window">
<field name="name">Pos Product Categories</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">pos.category</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="view_id" eval="False"/>
<field name="help" type="html">
<p class="oe_view_nocontent_create">
Click to define a new category.
</p><p>
Categories are used to browse your products through the
touchscreen interface.
</p><p>
If you put a photo on the category, the layout of the
touchscreen interface will automatically. We suggest not to put
a photo on categories for small (1024x768) screens.
</p>
</field>
</record>
<menuitem action="product_pos_category_action" id="menu_product_pos_category" parent="menu_point_of_sale_product" sequence="0" />
<!-- END -->
<record id="product_template_form_view" model="ir.ui.view">
<field name="name">product.template.form.inherit</field>
<field name="model">product.template</field>
<field name="inherit_id" ref="product.product_template_form_view"/>
<field name="arch" type="xml">
<group name="website_and_pos" position="inside">
<group name="pos" string="Point of Sale">
<field name="pos_categ_id"/>
<field name="available_in_pos"/>
<field name="to_weight" />
<field name="income_pdt"/>
@ -491,13 +546,10 @@
</group>
</group>
<field name="ean13" position="after">
<button name="edit_ean" type="object" string="Set a Custom EAN" class="oe_link oe_edit_only"/>
<button colspan="2" name="edit_ean" type="object" string="Set a Custom EAN" class="oe_link oe_edit_only"/>
</field>
</field>
</record>
<!-- Categories tree view -->
<menuitem action="product.product_public_category_action" id="menu_product_public_category" parent="menu_point_of_sale_product" sequence="0" />
<!-- END -->
<menuitem name="Configuration" parent="menu_point_root"

View File

@ -57,5 +57,5 @@ access_product_pricelist_manager,product.pricelist manager,product.model_product
access_pos_session_user,pos.session user,model_pos_session,group_pos_user,1,1,1,0
access_pos_config_user,pos.config user,model_pos_config,group_pos_user,1,1,1,0
access_ir_sequence_manager,ir.sequence manager,base.model_ir_sequence,group_pos_manager,1,1,1,1
access_product_category_pos_manager,product.public.category manager,product.model_product_public_category,group_pos_manager,1,1,1,1
access_product_category_pos_user,product.public.category user,product.model_product_public_category,group_pos_user,1,0,0,0
access_product_category_pos_manager,pos.category manager,model_pos_category,group_pos_manager,1,1,1,1
access_product_category_pos_user,pos.category user,model_pos_category,group_pos_user,1,0,0,0

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
57 access_pos_session_user pos.session user model_pos_session group_pos_user 1 1 1 0
58 access_pos_config_user pos.config user model_pos_config group_pos_user 1 1 1 0
59 access_ir_sequence_manager ir.sequence manager base.model_ir_sequence group_pos_manager 1 1 1 1
60 access_product_category_pos_manager product.public.category manager pos.category manager product.model_product_public_category model_pos_category group_pos_manager 1 1 1 1
61 access_product_category_pos_user product.public.category user pos.category user product.model_product_public_category model_pos_category group_pos_user 1 0 0 0

View File

@ -145,7 +145,7 @@ function openerp_pos_db(instance, module){
for(var i = 0, len = products.length; i < len; i++){
var product = products[i];
var search_string = this._product_search_string(product);
var categ_id = product.public_categ_id ? product.public_categ_id[0] : this.root_category_id;
var categ_id = product.pos_categ_id ? product.pos_categ_id[0] : this.root_category_id;
if (product.variants){
product.name = product.name+" ("+product.variants+")";
}

View File

@ -208,13 +208,13 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
}).then(function(packagings){
self.db.add_packagings(packagings);
return self.fetch('product.public.category', ['id','name','parent_id','child_id','image'])
return self.fetch('pos.category', ['id','name','parent_id','child_id','image'])
}).then(function(categories){
self.db.add_categories(categories);
return self.fetch(
'product.product',
['name', 'list_price','price','public_categ_id', 'taxes_id', 'ean13', 'default_code', 'variants',
['name', 'list_price','price','pos_categ_id', 'taxes_id', 'ean13', 'default_code', 'variants',
'to_weight', 'uom_id', 'uos_id', 'uos_coeff', 'mes_type', 'description_sale', 'description'],
[['sale_ok','=',true],['available_in_pos','=',true]],
{pricelist: self.pricelist.id} // context for price

View File

@ -436,7 +436,7 @@ function openerp_pos_widgets(instance, module){ //module is instance.point_of_sa
},
get_image_url: function(category){
return window.location.origin + '/web/binary/image?model=product.public.category&field=image_medium&id='+category.id;
return window.location.origin + '/web/binary/image?model=pos.category&field=image_medium&id='+category.id;
},
render_category: function( category, with_image ){

View File

@ -188,10 +188,13 @@ class product_pricelist(osv.osv):
products = map(lambda x: x[0], products_by_qty_by_partner)
currency_obj = self.pool.get('res.currency')
product_obj = self.pool.get('product.product')
product_obj = self.pool.get('product.template')
product_uom_obj = self.pool.get('product.uom')
price_type_obj = self.pool.get('product.price.type')
if not products:
return {}
version = False
for v in pricelist.version_id:
if ((v.date_start is False) or (v.date_start <= date)) and ((v.date_end is False) or (v.date_end >= date)):
@ -207,8 +210,13 @@ class product_pricelist(osv.osv):
categ = categ.parent_id
categ_ids = categ_ids.keys()
prod_ids = [x.id for x in products]
prod_tmpl_ids = [x.product_tmpl_id.id for x in products]
is_product_template = products[0]._name == "product.template"
if is_product_template:
prod_tmpl_ids = [tmpl.id for tmpl in products]
prod_ids = [product.id for product in tmpl.product_variant_ids for tmpl in products]
else:
prod_ids = [product.id for product in products]
prod_tmpl_ids = [product.product_tmpl_id.id for product in products]
# Load all rules
cr.execute(
@ -234,10 +242,17 @@ class product_pricelist(osv.osv):
for rule in items:
if rule.min_quantity and qty<rule.min_quantity:
continue
if rule.product_tmpl_id and product.product_tmpl_id.id<>rule.product_tmpl_id.id:
continue
if rule.product_id and product.id<>rule.product_id.id:
continue
if is_product_template:
if rule.product_tmpl_id and product.id<>rule.product_tmpl_id.id:
continue
if rule.product_id:
continue
else:
if rule.product_tmpl_id and product.product_tmpl_id.id<>rule.product_tmpl_id.id:
continue
if rule.product_id and product.id<>rule.product_id.id:
continue
if rule.categ_id:
cat = product.categ_id
while cat:

View File

@ -29,6 +29,7 @@ from openerp import tools
from openerp.osv import osv, fields
from openerp.tools.translate import _
from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT
import psycopg2
import openerp.addons.decimal_precision as dp
@ -273,7 +274,7 @@ class product_category(osv.osv):
_defaults = {
'type' : lambda *a : 'normal',
'type' : 'normal',
}
_parent_name = "parent_id"
@ -316,69 +317,78 @@ class produce_price_history(osv.osv):
}
class product_public_category(osv.osv):
_name = "product.public.category"
_description = "Public Category"
_order = "sequence, name"
#----------------------------------------------------------
# Product Attributes
#----------------------------------------------------------
class product_attribute(osv.osv):
_name = "product.attribute"
_description = "Product Attribute"
_columns = {
'name': fields.char('Name', translate=True, required=True),
'value_ids': fields.one2many('product.attribute.value', 'attribute_id', 'Values'),
}
_constraints = [
(osv.osv._check_recursion, 'Error ! You cannot create recursive categories.', ['parent_id'])
]
class product_attribute_value(osv.osv):
_name = "product.attribute.value"
_order = 'sequence'
def _get_price_extra(self, cr, uid, ids, name, args, context=None):
result = dict.fromkeys(ids, 0)
if not context.get('active_id'):
return result
def name_get(self, cr, uid, ids, context=None):
if not len(ids):
return []
reads = self.read(cr, uid, ids, ['name','parent_id'], context=context)
res = []
for record in reads:
name = record['name']
if record['parent_id']:
name = record['parent_id'][1]+' / '+name
res.append((record['id'], name))
return res
def _name_get_fnc(self, cr, uid, ids, prop, unknow_none, context=None):
res = self.name_get(cr, uid, ids, context=context)
return dict(res)
def _get_image(self, cr, uid, ids, name, args, context=None):
result = dict.fromkeys(ids, False)
for obj in self.browse(cr, uid, ids, context=context):
result[obj.id] = tools.image_get_resized_images(obj.image)
for price_id in obj.price_ids:
if price_id.product_tmpl_id.id == context.get('active_id'):
result[obj.id] = price_id.price_extra
break
return result
def _set_image(self, cr, uid, id, name, value, args, context=None):
return self.write(cr, uid, [id], {'image': tools.image_resize_image_big(value)}, context=context)
def _set_price_extra(self, cr, uid, id, name, value, args, context=None):
if 'active_id' not in context:
return None
p_obj = self.pool['product.attribute.price']
p_ids = p_obj.search(cr, uid, [('value_id', '=', id), ('product_tmpl_id', '=', context['active_id'])], context=context)
if p_ids:
p_obj.write(cr, uid, p_ids, {'price_extra': value}, context=context)
else:
p_obj.create(cr, uid, p_ids, {
'product_tmpl_id': context['active_id'],
'value_id': id,
'price_extra': value,
}, context=context)
_columns = {
'name': fields.char('Name', required=True, translate=True),
'complete_name': fields.function(_name_get_fnc, type="char", string='Name'),
'parent_id': fields.many2one('product.public.category','Parent Category', select=True),
'child_id': fields.one2many('product.public.category', 'parent_id', string='Children Categories'),
'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of product categories."),
# NOTE: there is no 'default image', because by default we don't show thumbnails for categories. However if we have a thumbnail
# for at least one category, then we display a default image on the other, so that the buttons have consistent styling.
# In this case, the default image is set by the js code.
# NOTE2: image: all image fields are base64 encoded and PIL-supported
'image': fields.binary("Image",
help="This field holds the image used as image for the cateogry, limited to 1024x1024px."),
'image_medium': fields.function(_get_image, fnct_inv=_set_image,
string="Medium-sized image", type="binary", multi="_get_image",
store={
'product.public.category': (lambda self, cr, uid, ids, c={}: ids, ['image'], 10),
},
help="Medium-sized image of the category. It is automatically "\
"resized as a 128x128px image, with aspect ratio preserved. "\
"Use this field in form views or some kanban views."),
'image_small': fields.function(_get_image, fnct_inv=_set_image,
string="Smal-sized image", type="binary", multi="_get_image",
store={
'product.public.category': (lambda self, cr, uid, ids, c={}: ids, ['image'], 10),
},
help="Small-sized image of the category. It is automatically "\
"resized as a 64x64px image, with aspect ratio preserved. "\
"Use this field anywhere a small image is required."),
'sequence': fields.integer('Sequence', help="Determine the display order"),
'name': fields.char('Value', translate=True, required=True),
'attribute_id': fields.many2one('product.attribute', 'Attribute', required=True),
'product_ids': fields.many2many('product.product', id1='att_id', id2='prod_id', string='Variants', readonly=True),
'price_extra': fields.function(_get_price_extra, type='float', string='Attribute Price Extra',
fnct_inv=_set_price_extra,
digits_compute=dp.get_precision('Product Price'),
help="Price Extra: Extra price for the variant with this attribute value on sale price. eg. 200 price extra, 1000 + 200 = 1200."),
'price_ids': fields.one2many('product.attribute.price', 'value_id', string='Attribute Prices', readonly=True),
}
_sql_constraints = [
('value_company_uniq', 'unique (name,attribute_id)', 'This attribute value already exists !')
]
_defaults = {
'price_extra': 0.0,
}
class product_attribute_price(osv.osv):
_name = "product.attribute.price"
_columns = {
'product_tmpl_id': fields.many2one('product.template', 'Product Template', required=True),
'value_id': fields.many2one('product.attribute.value', 'Product Attribute Value', required=True),
'price_extra': fields.float('Price Extra', digits_compute=dp.get_precision('Product Price')),
}
class product_attribute_line(osv.osv):
_name = "product.attribute.line"
_columns = {
'product_tmpl_id': fields.many2one('product.template', 'Product Template', required=True),
'attribute_id': fields.many2one('product.attribute', 'Attribute', required=True),
'value_ids': fields.many2many('product.attribute.value', id1='line_id', id2='val_id', string='Product Attribute Value'),
}
@ -399,6 +409,42 @@ class product_template(osv.osv):
def _set_image(self, cr, uid, id, name, value, args, context=None):
return self.write(cr, uid, [id], {'image': tools.image_resize_image_big(value)}, context=context)
def _is_product_variant(self, cr, uid, ids, name, arg, context=None):
return self.is_product_variant(cr, uid, ids, name, arg, context=context)
def is_product_variant(self, cr, uid, ids, name, arg, context=None):
prod = self.pool.get('product.product')
res = dict.fromkeys(ids, False)
ctx = dict(context, active_test=True)
for product in self.browse(cr, uid, ids, context=context):
res[product.id] = prod.search(cr, uid, [('product_tmpl_id','=',product.id)], context=ctx, count=True) == 1
return res
def _product_template_price(self, cr, uid, ids, name, arg, context=None):
plobj = self.pool.get('product.pricelist')
res = {}
quantity = context.get('quantity') or 1.0
pricelist = context.get('pricelist', False)
partner = context.get('partner', False)
if pricelist:
# Support context pricelists specified as display_name or ID for compatibility
if isinstance(pricelist, basestring):
pricelist_ids = plobj.name_search(
cr, uid, pricelist, operator='=', context=context, limit=1)
pricelist = pricelist_ids[0][0] if pricelist_ids else pricelist
if isinstance(pricelist, (int, long)):
products = self.browse(cr, uid, ids, context=context)
qtys = map(lambda x: (x, quantity, partner), products)
pl = plobj.browse(cr, uid, pricelist, context=context)
price = plobj._price_get_multi(cr,uid, pl, qtys, context=context)
for id in ids:
res[id] = price.get(id, 0.0)
for id in ids:
res.setdefault(id, 0.0)
return res
def get_history_price(self, cr, uid, product_tmpl, company_id, date=None, context=None):
if context is None:
context = {}
@ -421,6 +467,12 @@ class product_template(osv.osv):
'company_id': company_id,
}, context=context)
def _get_product_variant_count(self, cr, uid, ids, name, arg, context=None):
res = {}
for product in self.browse(cr, uid, ids):
res[product.id] = len(product.product_variant_ids)
return res
_columns = {
'name': fields.char('Name', required=True, translate=True, select=True),
'product_manager': fields.many2one('res.users','Product Manager'),
@ -434,9 +486,10 @@ class product_template(osv.osv):
"This description will be copied to every Sale Order, Delivery Order and Customer Invoice/Refund"),
'type': fields.selection([('consu', 'Consumable'),('service','Service')], 'Product Type', required=True, help="Consumable are product where you don't manage stock, a service is a non-material product provided by a company or an individual."),
'rental': fields.boolean('Can be Rent'),
'categ_id': fields.many2one('product.category','Category', required=True, change_default=True, domain="[('type','=','normal')]" ,help="Select category for the current product"),
'public_categ_id': fields.many2one('product.public.category','Public Category', help="Those categories are used to group similar products for public sales (eg.: point of sale, e-commerce)."),
'categ_id': fields.many2one('product.category','Internal Category', required=True, change_default=True, domain="[('type','=','normal')]" ,help="Select category for the current product"),
'price': fields.function(_product_template_price, type='float', string='Price', digits_compute=dp.get_precision('Product Price')),
'list_price': fields.float('Sale Price', digits_compute=dp.get_precision('Product Price'), help="Base price to compute the customer price. Sometimes called the catalog price."),
'lst_price' : fields.related('list_price', type="float", string='Public Price', digits_compute=dp.get_precision('Product Price')),
'standard_price': fields.property(type = 'float', digits_compute=dp.get_precision('Product Price'),
help="Cost price of the product template used for standard stock valuation in accounting and used as a base price on purchase orders.",
groups="base.group_user", string="Cost Price"),
@ -445,6 +498,7 @@ class product_template(osv.osv):
'weight_net': fields.float('Net Weight', digits_compute=dp.get_precision('Stock Weight'), help="The net weight in Kg."),
'warranty': fields.float('Warranty'),
'sale_ok': fields.boolean('Can be Sold', help="Specify if the product can be selected in a sales order line."),
'pricelist_id': fields.dummy(string='Pricelist', relation='product.pricelist', type='many2one'),
'state': fields.selection([('',''),
('draft', 'In Development'),
('sellable','Normal'),
@ -458,13 +512,12 @@ class product_template(osv.osv):
help='Coefficient to convert default Unit of Measure to Unit of Sale\n'
' uos = uom * coeff'),
'mes_type': fields.selection((('fixed', 'Fixed'), ('variable', 'Variable')), 'Measure Type'),
'seller_ids': fields.one2many('product.supplierinfo', 'product_tmpl_id', 'Supplier'),
'company_id': fields.many2one('res.company', 'Company', select=1),
# image: all image fields are base64 encoded and PIL-supported
'image': fields.binary("Image",
help="This field holds the image used as image for the product, limited to 1024x1024px."),
'image_medium': fields.function(_get_image, fnct_inv=_set_image,
string="Medium-sized image", type="binary", multi="_get_image",
string="Medium-sized image", type="binary", multi="_get_image",
store={
'product.template': (lambda self, cr, uid, ids, c={}: ids, ['image'], 10),
},
@ -479,13 +532,64 @@ class product_template(osv.osv):
help="Small-sized image of the product. It is automatically "\
"resized as a 64x64px image, with aspect ratio preserved. "\
"Use this field anywhere a small image is required."),
'product_variant_ids': fields.one2many('product.product', 'product_tmpl_id', 'Product Variants', required=True),
'packaging' : fields.one2many('product.packaging', 'product_id', 'Logistical Units',
help="Gives the different ways to package the same product. This has no impact on the picking order and is mainly used if you use the EDI module."),
'seller_ids': fields.one2many('product.supplierinfo', 'product_tmpl_id', 'Supplier'),
'seller_delay': fields.related('seller_ids','delay', type='integer', string='Supplier Lead Time',
help="This is the average delay in days between the purchase order confirmation and the reception of goods for this product and for the default supplier. It is used by the scheduler to order requests based on reordering delays."),
'seller_qty': fields.related('seller_ids','qty', type='float', string='Supplier Quantity',
help="This is minimum quantity to purchase from Main Supplier."),
'seller_id': fields.related('seller_ids','name', type='many2one', relation='res.partner', string='Main Supplier',
help="Main Supplier who has highest priority in Supplier List."),
'active': fields.boolean('Active', help="If unchecked, it will allow you to hide the product without removing it."),
'color': fields.integer('Color Index'),
'is_product_variant': fields.function( _is_product_variant, type='boolean', string='Only one product variant'),
'attribute_line_ids': fields.one2many('product.attribute.line', 'product_tmpl_id', 'Product Attributes'),
'product_variant_ids': fields.one2many('product.product', 'product_tmpl_id', 'Products', required=True),
'product_variant_count': fields.function( _get_product_variant_count, type='integer', string='# of Product Variants'),
# related to display product product information if is_product_variant
'ean13': fields.related('product_variant_ids', 'ean13', type='char', string='EAN13 Barcode'),
'default_code': fields.related('product_variant_ids', 'default_code', type='char', string='Internal Reference'),
}
def _price_get_list_price(self, product):
return 0.0
def _price_get(self, cr, uid, products, ptype='list_price', context=None):
if context is None:
context = {}
if 'currency_id' in context:
pricetype_obj = self.pool.get('product.price.type')
price_type_id = pricetype_obj.search(cr, uid, [('field','=',ptype)])[0]
price_type_currency_id = pricetype_obj.browse(cr,uid,price_type_id).currency_id.id
res = {}
product_uom_obj = self.pool.get('product.uom')
for product in products:
res[product.id] = product[ptype] or 0.0
if ptype == 'list_price':
res[product.id] += product._name == "product.product" and product.price_extra or 0.0
if 'uom' in context:
uom = product.uom_id or product.uos_id
res[product.id] = product_uom_obj._compute_price(cr, uid,
uom.id, res[product.id], context['uom'])
# Convert from price_type currency to asked one
if 'currency_id' in context:
# Take the price_type currency from the product field
# This is right cause a field cannot be in more than one currency
res[product.id] = self.pool.get('res.currency').compute(cr, uid, price_type_currency_id,
context['currency_id'], res[product.id],context=context)
return res
def _get_uom_id(self, cr, uid, *args):
cr.execute('select id from product_uom order by id limit 1')
res = cr.fetchone()
return res and res[0] or False
return self.pool["product.uom"].search(cr, uid, [], limit=1, order='id')[0]
def _default_category(self, cr, uid, context=None):
if context is None:
@ -505,9 +609,68 @@ class product_template(osv.osv):
return {'value': {'uom_po_id': uom_id}}
return {}
def create_variant_ids(self, cr, uid, ids, context=None):
product_obj = self.pool.get("product.product")
ctx = context and context.copy() or {}
if ctx.get("create_product_variant"):
return None
ctx.update(active_test=False, create_product_variant=True)
tmpl_ids = self.browse(cr, uid, ids, context=ctx)
for tmpl_id in tmpl_ids:
# list of values combination
all_variants = [[]]
for variant_id in tmpl_id.attribute_line_ids:
if len(variant_id.value_ids) > 1:
temp_variants = []
for value_id in variant_id.value_ids:
for variant in all_variants:
temp_variants.append(variant + [int(value_id)])
all_variants = temp_variants
# check product
variant_ids_to_active = []
variants_active_ids = []
variants_inactive = []
for product_id in tmpl_id.product_variant_ids:
variants = map(int,product_id.attribute_value_ids)
if variants in all_variants:
variants_active_ids.append(product_id.id)
all_variants.pop(all_variants.index(variants))
if not product_id.active:
variant_ids_to_active.append(product_id.id)
else:
variants_inactive.append(product_id)
if variant_ids_to_active:
product_obj.write(cr, uid, variant_ids_to_active, {'active': True}, context=ctx)
# create new product
for variant_ids in all_variants:
values = {
'product_tmpl_id': tmpl_id.id,
'attribute_value_ids': [(6, 0, variant_ids)]
}
id = product_obj.create(cr, uid, values, context=ctx)
variants_active_ids.append(id)
# unlink or inactive product
for variant_id in map(int,variants_inactive):
try:
with cr.savepoint():
product_obj.unlink(cr, uid, [variant_id], context=ctx)
except (psycopg2.Error, osv.except_osv):
product_obj.write(cr, uid, [variant_id], {'active': False}, context=ctx)
pass
return True
def create(self, cr, uid, vals, context=None):
''' Store the initial standard price in order to be able to retrieve the cost of a product template for a given date'''
product_template_id = super(product_template, self).create(cr, uid, vals, context=context)
if not context or "create_product_product" not in context:
self.create_variant_ids(cr, uid, [product_template_id], context=context)
self._set_standard_price(cr, uid, product_template_id, vals.get('standard_price', 0.0), context=context)
return product_template_id
@ -524,7 +687,17 @@ class product_template(osv.osv):
if 'standard_price' in vals:
for prod_template_id in ids:
self._set_standard_price(cr, uid, prod_template_id, vals['standard_price'], context=context)
return super(product_template, self).write(cr, uid, ids, vals, context=context)
res = super(product_template, self).write(cr, uid, ids, vals, context=context)
if 'attribute_line_ids' in vals or vals.get('active'):
self.create_variant_ids(cr, uid, ids, context=context)
if 'active' in vals and not vals.get('active'):
ctx = context and context.copy() or {}
ctx.update(active_test=False)
product_ids = []
for product in self.browse(cr, uid, ids, context=ctx):
product_ids = map(int,product.product_variant_ids)
self.pool.get("product.product").write(cr, uid, product_ids, {'active': vals.get('active')}, context=ctx)
return res
def copy(self, cr, uid, id, default=None, context=None):
if default is None:
@ -544,6 +717,7 @@ class product_template(osv.osv):
'mes_type': 'fixed',
'categ_id' : _default_category,
'type' : 'consu',
'active': True,
}
def _check_uom(self, cursor, user, ids, context=None):
@ -579,14 +753,6 @@ class product_product(osv.osv):
_inherit = ['mail.thread']
_order = 'default_code,name_template'
def view_header_get(self, cr, uid, view_id, view_type, context=None):
if context is None:
context = {}
res = super(product_product, self).view_header_get(cr, uid, view_id, view_type, context)
if (context.get('categ_id', False)):
return _('Products: ') + self.pool.get('product.category').browse(cr, uid, context['categ_id'], context=context).name
return res
def _product_price(self, cr, uid, ids, name, arg, context=None):
plobj = self.pool.get('product.pricelist')
res = {}
@ -613,11 +779,19 @@ class product_product(osv.osv):
res.setdefault(id, 0.0)
return res
def view_header_get(self, cr, uid, view_id, view_type, context=None):
if context is None:
context = {}
res = super(product_product, self).view_header_get(cr, uid, view_id, view_type, context)
if (context.get('categ_id', False)):
return _('Products: ') + self.pool.get('product.category').browse(cr, uid, context['categ_id'], context=context).name
return res
def _product_lst_price(self, cr, uid, ids, name, arg, context=None):
res = {}
product_uom_obj = self.pool.get('product.uom')
for id in ids:
res.setdefault(id, 0.0)
res = dict.fromkeys(ids, 0.0)
for product in self.browse(cr, uid, ids, context=context):
if 'uom' in context:
uom = product.uos_id or product.uom_id
@ -625,20 +799,17 @@ class product_product(osv.osv):
uom.id, product.list_price, context['uom'])
else:
res[product.id] = product.list_price
res[product.id] = (res[product.id] or 0.0) * (product.price_margin or 1.0) + product.price_extra
price_extra = 0.0
for variant_id in product.attribute_value_ids:
price_extra += variant_id.price_extra
res[product.id] = (res[product.id] or 0.0) + price_extra
return res
def _save_product_lst_price(self, cr, uid, product_id, field_name, field_value, arg, context=None):
field_value = field_value or 0.0
product = self.browse(cr, uid, product_id, context=context)
list_price = (field_value - product.price_extra) / (product.price_margin or 1.0)
return self.write(cr, uid, [product_id], {'list_price': list_price}, context=context)
def _get_partner_code_name(self, cr, uid, ids, product, partner_id, context=None):
for supinfo in product.seller_ids:
if supinfo.name.id == partner_id:
return {'code': supinfo.product_code or product.default_code, 'name': supinfo.product_name or product.name, 'variants': ''}
res = {'code': product.default_code, 'name': product.name, 'variants': product.variants}
return {'code': supinfo.product_code or product.default_code, 'name': supinfo.product_name or product.name}
res = {'code': product.default_code, 'name': product.name}
return res
def _product_code(self, cr, uid, ids, name, arg, context=None):
@ -655,48 +826,15 @@ class product_product(osv.osv):
context = {}
for p in self.browse(cr, uid, ids, context=context):
data = self._get_partner_code_name(cr, uid, [], p, context.get('partner_id', None), context=context)
if not data['variants']:
data['variants'] = p.variants
if not data['code']:
data['code'] = p.code
if not data['name']:
data['name'] = p.name
res[p.id] = (data['code'] and ('['+data['code']+'] ') or '') + \
(data['name'] or '') + (data['variants'] and (' - '+data['variants']) or '')
res[p.id] = (data['code'] and ('['+data['code']+'] ') or '') + (data['name'] or '')
return res
def _is_only_child(self, cr, uid, ids, name, arg, context=None):
res = dict.fromkeys(ids, True)
for product in self.browse(cr, uid, ids, context=context):
if product.product_tmpl_id and len(product.product_tmpl_id.product_variant_ids) > 1:
res[product.id] = False
return res
def _get_main_product_supplier(self, cr, uid, product, context=None):
"""Determines the main (best) product supplier for ``product``,
returning the corresponding ``supplierinfo`` record, or False
if none were found. The default strategy is to select the
supplier with the highest priority (i.e. smallest sequence).
:param browse_record product: product to supply
:rtype: product.supplierinfo browse_record or False
"""
sellers = [(seller_info.sequence, seller_info)
for seller_info in product.seller_ids or []
if seller_info and isinstance(seller_info.sequence, (int, long))]
return sellers and sellers[0][1] or False
def _calc_seller(self, cr, uid, ids, fields, arg, context=None):
result = {}
for product in self.browse(cr, uid, ids, context=context):
main_supplier = self._get_main_product_supplier(cr, uid, product, context=context)
result[product.id] = {
'seller_info_id': main_supplier and main_supplier.id or False,
'seller_delay': main_supplier.delay if main_supplier else 1,
'seller_qty': main_supplier and main_supplier.qty or 0.0,
'seller_id': main_supplier and main_supplier.name.id or False
}
return result
def is_product_variant(self, cr, uid, ids, name, arg, context=None):
return dict.fromkeys(ids, True)
def _get_name_template_ids(self, cr, uid, ids, context=None):
result = set()
@ -705,39 +843,68 @@ class product_product(osv.osv):
result.add(el)
return list(result)
def _get_image_variant(self, cr, uid, ids, name, args, context=None):
result = dict.fromkeys(ids, False)
for obj in self.browse(cr, uid, ids, context=context):
result[obj.id] = obj.image_variant or getattr(obj.product_tmpl_id, name)
return result
def _set_image_variant(self, cr, uid, id, name, value, args, context=None):
image = tools.image_resize_image_big(value)
res = self.write(cr, uid, [id], {'image_variant': image}, context=context)
product = self.browse(cr, uid, id, context=context)
if not product.product_tmpl_id.image:
product.write({'image_variant': None}, context=context)
product.product_tmpl_id.write({'image': image}, context=context)
return res
def _get_price_extra(self, cr, uid, ids, name, args, context=None):
result = dict.fromkeys(ids, False)
for product in self.browse(cr, uid, ids, context=context):
price_extra = 0.0
for variant_id in product.attribute_value_ids:
for price_id in variant_id.price_ids:
if price_id.product_tmpl_id.id == product.product_tmpl_id.id:
price_extra += price_id.price_extra
result[product.id] = price_extra
return result
_columns = {
'price': fields.function(_product_price, fnct_inv=_save_product_lst_price, type='float', string='Price', digits_compute=dp.get_precision('Product Price')),
'lst_price' : fields.function(_product_lst_price, fnct_inv=_save_product_lst_price, type='float', string='Public Price', digits_compute=dp.get_precision('Product Price')),
'price': fields.function(_product_price, type='float', string='Price', digits_compute=dp.get_precision('Product Price')),
'price_extra': fields.function(_get_price_extra, type='float', string='Variant Extra Price', help="This is le sum of the extra price of all attributes"),
'lst_price': fields.function(_product_lst_price, type='float', string='Public Price', digits_compute=dp.get_precision('Product Price')),
'code': fields.function(_product_code, type='char', string='Internal Reference'),
'partner_ref' : fields.function(_product_partner_ref, type='char', string='Customer ref'),
'default_code' : fields.char('Internal Reference', select=True),
'active': fields.boolean('Active', help="If unchecked, it will allow you to hide the product without removing it."),
'variants': fields.char('Variants', translate=True),
'product_tmpl_id': fields.many2one('product.template', 'Product Template', required=True, ondelete="cascade", select=True),
'is_only_child': fields.function(
_is_only_child, type='boolean', string='Sole child of the parent template'),
'ean13': fields.char('EAN13 Barcode', size=13, help="International Article Number used for product identification."),
'packaging': fields.one2many('product.packaging', 'product_id', 'Packaging', help="Gives the different ways to package the same product. This has no impact on the picking order and is mainly used if you use the EDI module."),
'price_extra': fields.float('Variant Price Extra', digits_compute=dp.get_precision('Product Price'), help="Price Extra: Extra price for the variant on sale price. eg. 200 price extra, 1000 + 200 = 1200."),
'price_margin': fields.float('Variant Price Margin', digits_compute=dp.get_precision('Product Price'), help="Price Margin: Margin in percentage amount on sale price for the variant. eg. 10 price margin, 1000 * 1.1 = 1100."),
'pricelist_id': fields.dummy(string='Pricelist', relation='product.pricelist', type='many2one'),
'name_template': fields.related('product_tmpl_id', 'name', string="Template Name", type='char', store={
'product.template': (_get_name_template_ids, ['name'], 10),
'product.product': (lambda self, cr, uid, ids, c=None: ids, [], 10),
}, select=True),
'color': fields.integer('Color Index'),
'seller_info_id': fields.function(_calc_seller, type='many2one', relation="product.supplierinfo", string="Supplier Info", multi="seller_info"),
'seller_delay': fields.function(_calc_seller, type='integer', string='Supplier Lead Time', multi="seller_info", help="This is the average delay in days between the purchase order confirmation and the reception of goods for this product and for the default supplier. It is used by the scheduler to order requests based on reordering delays."),
'seller_qty': fields.function(_calc_seller, type='float', string='Supplier Quantity', multi="seller_info", help="This is minimum quantity to purchase from Main Supplier."),
'seller_id': fields.function(_calc_seller, type='many2one', relation="res.partner", string='Main Supplier', help="Main Supplier who has highest priority in Supplier List.", multi="seller_info"),
'attribute_value_ids': fields.many2many('product.attribute.value', id1='prod_id', id2='att_id', string='Attributes', readonly=True),
# image: all image fields are base64 encoded and PIL-supported
'image_variant': fields.binary("Variant Image",
help="This field holds the image used as image for the product variant, limited to 1024x1024px."),
'image': fields.function(_get_image_variant, fnct_inv=_set_image_variant,
string="Big-sized image", type="binary",
help="Image of the product variant (Big-sized image of product template if false). It is automatically "\
"resized as a 1024x1024px image, with aspect ratio preserved."),
'image_small': fields.function(_get_image_variant, fnct_inv=_set_image_variant,
string="Small-sized image", type="binary",
help="Image of the product variant (Small-sized image of product template if false)."),
'image_medium': fields.function(_get_image_variant, fnct_inv=_set_image_variant,
string="Medium-sized image", type="binary",
help="Image of the product variant (Medium-sized image of product template if false)."),
}
_defaults = {
'active': lambda *a: 1,
'price_extra': lambda *a: 0.0,
'price_margin': lambda *a: 1.0,
'active': 1,
'color': 0,
'is_only_child': True,
}
def unlink(self, cr, uid, ids, context=None):
@ -767,8 +934,9 @@ class product_product(osv.osv):
def _check_ean_key(self, cr, uid, ids, context=None):
for product in self.read(cr, uid, ids, ['ean13'], context=context):
res = check_ean(product['ean13'])
return res
if not check_ean(product['ean13']):
return False
return True
_constraints = [(_check_ean_key, 'You provided an invalid "EAN13 Barcode" reference. You may use the "Internal Reference" field instead.', ['ean13'])]
@ -782,13 +950,12 @@ class product_product(osv.osv):
ids = [ids]
if not len(ids):
return []
def _name_get(d):
name = d.get('name','')
code = d.get('default_code',False)
if code:
name = '[%s] %s' % (code,name)
if d.get('variants'):
name = name + ' - %s' % (d['variants'],)
return (d['id'], name)
partner_id = context.get('partner_id', False)
@ -800,6 +967,8 @@ class product_product(osv.osv):
result = []
for product in self.browse(cr, SUPERUSER_ID, ids, context=context):
variant = ", ".join([v.name for v in product.attribute_value_ids])
name = variant and "%s (%s)" % (product.name, variant) or product.name
sellers = []
if partner_id:
sellers = filter(lambda x: x.name.id == partner_id, product.seller_ids)
@ -807,17 +976,15 @@ class product_product(osv.osv):
for s in sellers:
mydict = {
'id': product.id,
'name': s.product_name or product.name,
'name': s.product_name or name,
'default_code': s.product_code or product.default_code,
'variants': product.variants
}
result.append(_name_get(mydict))
else:
mydict = {
'id': product.id,
'name': product.name,
'name': name,
'default_code': product.default_code,
'variants': product.variants
}
result.append(_name_get(mydict))
return result
@ -855,44 +1022,7 @@ class product_product(osv.osv):
#
def price_get(self, cr, uid, ids, ptype='list_price', context=None):
products = self.browse(cr, uid, ids, context=context)
return self._price_get(cr, uid, products, ptype=ptype, context=context)
def _price_get(self, cr, uid, products, ptype='list_price', context=None):
if context is None:
context = {}
if 'currency_id' in context:
pricetype_obj = self.pool.get('product.price.type')
price_type_id = pricetype_obj.search(cr, uid, [('field','=',ptype)])[0]
price_type_currency_id = pricetype_obj.browse(cr,uid,price_type_id).currency_id.id
res = {}
# standard_price field can only be seen by users in base.group_user
# Thus, in order to compute the sale price from the cost price for users not in this group
# We fetch the standard price as the superuser
for product in products:
if ptype != 'standard_price':
res[product.id] = product[ptype] or 0.0
else:
res[product.id] = self.read(cr, SUPERUSER_ID, product.id, [ptype], context=context)[ptype] or 0.0
product_uom_obj = self.pool.get('product.uom')
for product in products:
if ptype == 'list_price':
res[product.id] = (res[product.id] * (product.price_margin or 1.0)) + \
product.price_extra
if 'uom' in context:
uom = product.uom_id or product.uos_id
res[product.id] = product_uom_obj._compute_price(cr, uid,
uom.id, res[product.id], context['uom'])
# Convert from price_type currency to asked one
if 'currency_id' in context:
# Take the price_type currency from the product field
# This is right cause a field cannot be in more than one currency
res[product.id] = self.pool.get('res.currency').compute(cr, uid, price_type_currency_id,
context['currency_id'], res[product.id],context=context)
return res
return self.pool.get("product.template")._price_get(cr, uid, products, ptype=ptype, context=context)
def copy(self, cr, uid, id, default=None, context=None):
if context is None:
@ -923,6 +1053,12 @@ class product_product(osv.osv):
'res_id': product.product_tmpl_id.id,
'target': 'new'}
def create(self, cr, uid, vals, context=None):
if context is None:
context = {}
ctx = dict(context or {}, create_product_product=True)
return super(product_product, self).create(cr, uid, vals, context=ctx)
class product_packaging(osv.osv):
_name = "product.packaging"
@ -948,8 +1084,9 @@ class product_packaging(osv.osv):
def _check_ean_key(self, cr, uid, ids, context=None):
for pack in self.browse(cr, uid, ids, context=context):
res = check_ean(pack.ean)
return res
if not check_ean(pack.ean):
return False
return True
_constraints = [(_check_ean_key, 'Error: Invalid ean code', ['ean'])]
@ -969,8 +1106,8 @@ class product_packaging(osv.osv):
return (res and res[0]) or False
_defaults = {
'rows' : lambda *a : 3,
'sequence' : lambda *a : 1,
'rows' : 3,
'sequence' : 1,
'ul' : _get_1st_ul,
}
@ -1010,9 +1147,9 @@ class product_supplierinfo(osv.osv):
'company_id':fields.many2one('res.company','Company',select=1),
}
_defaults = {
'qty': lambda *a: 0.0,
'sequence': lambda *a: 1,
'delay': lambda *a: 1,
'qty': 0.0,
'sequence': 1,
'delay': 1,
'company_id': lambda self,cr,uid,c: self.pool.get('res.company')._company_default_get(cr, uid, 'product.supplierinfo', context=c),
}
def price_get(self, cr, uid, supplier_ids, product_id, product_qty=1, context=None):

View File

@ -255,10 +255,5 @@ parameter) will see those record just disappear.
<field name="type">service</field>
<field name="sale_ok" eval="False"/>
</record>
<!-- Product Public Categories -->
<record id="categ_others" model="product.public.category">
<field name="name">Others</field>
</record>
</data>
</openerp>

File diff suppressed because it is too large Load Diff

View File

@ -1,198 +1,140 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<menuitem id="prod_config_main" name="Products" parent="base.menu_base_config" sequence="70"/>
<menuitem id="base.menu_product" name="Product Variants" parent="base.menu_base_partner" sequence="9"/>
<record id="product_search_form_view" model="ir.ui.view">
<field name="name">product.search.form</field>
<field name="model">product.product</field>
<!-- templates -->
<record id="product_template_search_view" model="ir.ui.view">
<field name="name">product.template.search</field>
<field name="model">product.template</field>
<field name="arch" type="xml">
<search string="Product">
<field name="name" string="Product" filter_domain="['|','|',('name','ilike',self),('default_code','ilike',self),('variants','ilike',self)]"/>
<filter string="Services" icon="terp-accessories-archiver" domain="[('type','=','service')]"/>
<filter string="Consumable" name="consumable" icon="terp-accessories-archiver" domain="[('type','=','consu')]" help="Consumable products"/>
<separator/>
<filter string="Can be Sold" name="filter_to_sell" icon="terp-accessories-archiver-minus" domain="[('sale_ok','=',1)]"/>
<field name="categ_id" operator="child_of"/>
<group expand="0" string="Context...">
<field name="pricelist_id" widget="selection" context="{'pricelist': self}" filter_domain="[]" groups="product.group_sale_pricelist"/> <!-- Keep widget=selection on this field to pass numeric `self` value, which is not the case for regular m2o widgets! -->
<field name="company_id" groups="base.group_multi_company"/>
</group>
<group expand='0' string='Group by...'>
<filter string='Category' icon="terp-stock_symbol-selection" domain="[]" context="{'group_by' : 'categ_id'}"/>
<field name="name" string="Product"/>
<filter string="Services" icon="terp-accessories-archiver" domain="[('type','=','service')]"/>
<filter string="Consumable" name="consumable" icon="terp-accessories-archiver" domain="[('type','=','consu')]" help="Consumable products"/>
<separator/>
<filter string="Can be Sold" name="filter_to_sell" icon="terp-accessories-archiver-minus" domain="[('sale_ok','=',1)]"/>
<field string="Product Variant" name="product_variant_ids" filter_domain="['|', ('product_variant_ids.name','ilike',self), ('product_variant_ids.attribute_value_ids.name','ilike',self)]"/>
<field name="company_id"/>
<field name="pricelist_id" widget="selection" context="{'pricelist': self}" filter_domain="[]" groups="product.group_sale_pricelist"/> <!-- Keep widget=selection on this field to pass numeric `self` value, which is not the case for regular m2o widgets! -->
<group expand='0' string='Group by...'>
<filter string='Default Unit of Measure' icon="terp-mrp" domain="[]" context="{'group_by' : 'uom_id'}"/>
<filter string='Type' icon="terp-stock_symbol-selection" domain="[]" context="{'group_by' : 'type'}"/>
<filter string='Company' icon="terp-go-home" domain="[]" context="{'group_by' : 'company_id'}" groups="base.group_multi_company"/>
<filter string='Template' name="template_id" domain="[]" context="{'group_by' : 'product_tmpl_id'}" groups="product.group_product_variant"/>
</group>
</group>
</search>
</field>
</record>
<record id="product_product_tree_view" model="ir.ui.view">
<field name="name">product.product.tree</field>
<field name="model">product.product</field>
<field eval="7" name="priority"/>
<record id="product_template_tree_view" model="ir.ui.view">
<field name="name">product.template.product.tree</field>
<field name="model">product.template</field>
<field name="arch" type="xml">
<tree string="Products">
<field name="default_code"/>
<tree string="Product">
<field name="name"/>
<field name="variants" groups="product.group_product_variant"/>
<field name="categ_id" invisible="1"/>
<field name="type" invisible="1"/>
<field name="uom_id" string="Unit of Measure" groups="product.group_uom"/>
<field name="lst_price"/>
<field name="price" invisible="not context.get('pricelist',False)"/>
<field name="standard_price" invisible="1"/>
<field name="categ_id"/>
<field name="type"/>
<field name="state"/>
<field name="company_id" groups="base.group_multi_company" invisible="1"/>
<field name="product_tmpl_id" invisible="1"/>
<field name="uom_id" invisible="1"/>
</tree>
</field>
</record>
<record id="product_normal_form_view" model="ir.ui.view">
<field name="name">product.normal.form</field>
<field name="model">product.product</field>
<field eval="7" name="priority"/>
<record id="product_template_form_view" model="ir.ui.view">
<field name="name">product.template.product.form</field>
<field name="model">product.template</field>
<field name="arch" type="xml">
<form string="Product" version="7.0">
<field name="is_product_variant" invisible="1"/>
<sheet>
<field name="is_only_child" invisible="1"/>
<field name="image_medium" widget="image" class="oe_avatar oe_left"
attrs="{'readonly': [('is_only_child', '=', False)]}"/>
<div class="oe_title">
<div class="oe_edit_only">
<label for="name" name='label_name' string="Product Name"
attrs="{'invisible': [('is_only_child', '=', False)]}"/>
<label for="name" name='label_name' string="Product Template"
attrs="{'invisible': [('is_only_child', '=', True)]}"/>
</div>
<h1>
<field name="name" class="oe_inline"
attrs="{'required': [('is_only_child', '=', True)],
'invisible': [('is_only_child', '=', False)]}"/>
<field name="product_tmpl_id" class="oe_inline" readonly="1" required="0"
attrs="{'invisible': [('is_only_child', '=', True)]}"/>
<span groups="product.group_product_variant"
attrs="{'invisible': [('is_only_child', '=', True), ('variants', '=', False)]}">
- <field name="variants" placeholder="Variant Name" class="oe_inline"/>
</span>
</h1>
<span attrs="{'invisible': [('is_only_child', '=', True)]}">
<p>Generic product settings are managed on
<button name="open_product_template" type="object" string="the template" class="oe_link"/>
</p>
</span>
<label for="categ_id" class="oe_edit_only"/>
<h2><field name="categ_id" attrs="{'readonly': [('is_only_child', '=', False)]}"/></h2>
<label for="public_categ_id" class="oe_edit_only"/>
<h3><field name="public_categ_id" attrs="{'readonly': [('is_only_child', '=', False)]}"/></h3>
<div name="options" groups="base.group_user">
<field name="sale_ok"/>
<label for="sale_ok"/>
</div>
<field name="id" invisible="True"/>
<div class="oe_right oe_button_box" style="width: 300px;" name="buttons">
</div>
<div class="oe_right oe_button_box" name="buttons">
<div class="oe_left" style="width: 500px;">
<field name="image_medium" widget="image" class="oe_avatar oe_left"/>
<div class="oe_title" style="width: 390px;">
<label class="oe_edit_only" for="name" string="Product Name"/>
<h1><field name="name" class="oe_inline"/></h1>
</div>
<div class="oe_left" name="options" groups="base.group_user">
<div>
<field name="sale_ok"/>
<label for="sale_ok"/>
</div>
</div>
</div>
<notebook>
<page string="Information">
<group>
<group colspan="4">
<group>
<field name="type"
attrs="{'invisible': [('is_only_child', '=', False)]}"/>
<field name="uom_id" on_change="onchange_uom(uom_id,uom_po_id)" groups="product.group_uom"
attrs="{'invisible': [('is_only_child', '=', False)]}"/>
<field name="lst_price" string="Public Sale Price"
attrs="{'readonly': [('is_only_child', '=', False)]}"/>
<field name="price_extra"
attrs="{'invisible': [('is_only_child', '=', True)]}"/>
<field name="type"/>
<field name="uom_id" on_change="onchange_uom(uom_id,uom_po_id)" groups="product.group_uom"/>
<field name="lst_price"/>
</group>
<group>
<field name="active"/>
<field name="ean13"/>
<field name="default_code"/>
<label for="ean13"/>
<div name="ean">
<field name="ean13" placeholder="e.g. 5901234123457"/>
</div>
<field name="company_id" groups="base.group_multi_company" widget="selection"
attrs="{'invisible': [('is_only_child', '=', False)]}"/>
<field name="active" />
</group>
</group>
<field name="description" placeholder="describe the product characteristics..."
attrs="{'readonly': [('is_only_child', '=', False)]}"/>
<group colspan="4">
<field name="company_id" groups="base.group_multi_company" widget="selection"/>
</group>
<field name="description" placeholder="describe the product characteristics..."/>
</page>
<page string="Procurements" groups="base.group_user">
<group name="procurement">
<group name="general">
<field name="standard_price" attrs="{'readonly': [('is_only_child', '=', False)]}"/>
<field name="standard_price"/>
</group>
<group name="procurement_uom" groups="product.group_uom" string="Purchase">
<field name="uom_po_id"
attrs="{'readonly': [('is_only_child', '=', False)]}"/>
<field name="uom_po_id"/>
</group>
</group>
<separator string="Suppliers"/>
<field name="seller_ids"/>
<separator string="Description for Suppliers"/>
<field name="description_purchase" placeholder="This note will be displayed on requests for quotation..."/>
</page>
<page string="Inventory" groups="base.group_user">
<page string="Inventory">
<group name="inventory">
<group name="status" string="Status">
<field name="state"
attrs="{'readonly': [('is_only_child', '=', False)]}"/>
<field name="product_manager"
attrs="{'readonly': [('is_only_child', '=', False)]}"
context="{'default_groups_ref': ['base.group_user', 'base.group_partner_manager', 'base.group_sale_manager']}"/>
<group name="status" string="Status">
<field name="state"/>
<field name="product_manager"/>
</group>
<group name="Weights" groups="product.group_stock_packaging" string="Weights">
<field digits="(14, 3)" name="volume"
attrs="{'readonly': ['|', ('type','=','service'), ('is_only_child', '=', False)]}"/>
<field name="weight"
attrs="{'readonly': ['|', ('type','=','service'), ('is_only_child', '=', False)]}"/>
<field name="weight_net"
attrs="{'readonly': ['|', ('type','=','service'), ('is_only_child', '=', False)]}"/>
<group name="weight" string="Weights">
<field digits="(14, 3)" name="volume" attrs="{'readonly':[('type','=','service')]}"/>
<field digits="(14, 3)" name="weight" attrs="{'readonly':[('type','=','service')]}"/>
<field digits="(14, 3)" name="weight_net" attrs="{'readonly':[('type','=','service')]}"/>
</group>
</group>
</page>
<page string="Sales" attrs="{'invisible':[('sale_ok','=',False)]}">
<group name="sale">
<group string="Sale Conditions">
<group name="sale_condition" string="Sale Conditions" colspan="3">
<label for="warranty"/>
<div attrs="{'readonly': [('is_only_child', '=', False)]}">
<div>
<field name="warranty" class="oe_inline"/> months
</div>
</group>
<group groups="product.group_uos" string="Unit of Measure">
<field name="uos_id"
attrs="{'readonly': [('is_only_child', '=', False)]}"/>
<field name="uos_coeff"
attrs="{'readonly': [('is_only_child', '=', False)]}"/>
<field name="mes_type"
attrs="{'readonly': [('is_only_child', '=', False)]}"/>
<field name="uos_id"/>
<field name="uos_coeff"/>
<field name="mes_type"/>
</group>
</group>
<separator string="Packages" groups="product.group_stock_packaging"/>
<field name="packaging" groups="product.group_stock_packaging" attrs="{'readonly': [('is_only_child', '=', False)]}">
<form string="Packaging" version="7.0">
<group col="4">
<field name="ean"/>
<field name="sequence" invisible="1"/>
<newline/>
<field name="qty"/>
<field name="ul"/>
<separator colspan="4" string="Palletization"/>
<field name="ul_qty"/>
<field name="ul_container" domain="[('type', '=', 'pallet')]" context="{'default_type': 'pallet'}"/>
<field name="rows"/>
<field name="weight"/>
</group>
<separator colspan="4" string="Description"/>
<field name="name"/>
</form>
</field>
<group name="website_and_pos" col="2">
</group>
<separator string="Description for Quotations"/>
<field name="description_sale" placeholder="note to be displayed on quotations..."
attrs="{'readonly': [('is_only_child', '=', False)]}"/>
<field name="description_sale" placeholder="note to be displayed on quotations..."/>
</page>
<page name="variants" string="Variants">
<field name="attribute_line_ids" widget="one2many_list">
<tree string="Variants" editable="bottom">
<field name="attribute_id"/>
<field name="value_ids" widget="many2many_tags" domain="[('attribute_id', '=', attribute_id)]" context="{'default_attribute_id': attribute_id}"/>
</tree>
</field>
</page>
</notebook>
</sheet>
@ -204,24 +146,21 @@
</field>
</record>
<!-- Product Kanban View -->
<record model="ir.ui.view" id="product.product_kanban_view">
<field name="name">Product Kanban</field>
<field name="model">product.product</field>
<record id="product_template_kanban_view" model="ir.ui.view">
<field name="name">Product.template.product.kanban</field>
<field name="model">product.template</field>
<field name="arch" type="xml">
<kanban>
<field name="color"/>
<field name="type"/>
<field name="list_price"/>
<field name="image_small"/>
<field name="lst_price"/>
<templates>
<t t-name="kanban-box">
<div class="oe_kanban_vignette oe_semantic_html_override">
<a type="open"><img t-att-src="kanban_image('product.product', 'image_small', record.id.value)" class="oe_kanban_image"/></a>
<a type="open"><img t-att-src="kanban_image('product.template', 'image_small', record.id.value)" class="oe_kanban_image"/></a>
<div class="oe_kanban_details">
<h4>
<a type="open">
<t t-if="record.code.raw_value">[<field name="code"/>]</t> <field name="name"/> <t t-if="record.variants.raw_value">(<field name="variants"/>)</t>
<field name="name"/>
</a>
</h4>
<div name="tags"/>
@ -236,13 +175,134 @@
</field>
</record>
<record id="product_normal_action" model="ir.actions.act_window">
<record id="product_template_action" model="ir.actions.act_window">
<field name="name">Products</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">product.product</field>
<field name="res_model">product.template</field>
<field name="view_mode">kanban,tree,form</field>
<field name="view_type">form</field>
<field name="view_id" ref="product_template_kanban_view"/>
</record>
<menuitem action="product_template_action"
id="menu_product_template_action"
parent="base.menu_product" sequence="1" />
<!-- variants -->
<record id="variants_template_tree_view" model="ir.ui.view">
<field name="name">variants.template.tree</field>
<field name="model">product.attribute.value</field>
<field name="arch" type="xml">
<tree string="Variant Values" editable="top">
<field name="sequence" widget="handle"/>
<field name="attribute_id"/>
<field name="name"/>
<field name="price_extra"/>
</tree>
</field>
</record>
<record id="variants_template_action" model="ir.actions.act_window">
<field name="name">Variant Values</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">product.attribute.value</field>
<field name="view_mode">tree</field>
<field name="view_type">form</field>
<field name="domain">[('product_ids.product_tmpl_id', '=', active_id)]</field>
<field name="context">{'default_product_tmpl_id': active_id}</field>
</record>
<!-- product product -->
<menuitem id="prod_config_main" name="Product Variants" parent="base.menu_base_config" sequence="70" groups="base.group_no_one"/>
<record id="product_search_form_view" model="ir.ui.view">
<field name="name">product.product.search</field>
<field name="model">product.product</field>
<field name="mode">primary</field>
<field name="inherit_id" ref="product.product_template_search_view"/>
<field name="arch" type="xml">
<field name="name" position="replace">
<field name="name" string="Product" filter_domain="['|',('default_code','ilike',self),('name','ilike',self)]"/>
</field>
<field name="product_variant_ids" position="replace">
<field name="attribute_value_ids"/>
</field>
<field name="name" position="after">
<field name="product_tmpl_id" string="Product Template"/>
</field>
<xpath expr="//group[@string='Group by...']" position="inside">
<filter string='Product Template' name="template_id" domain="[]" context="{'group_by' : 'product_tmpl_id'}"/>
</xpath>
</field>
</record>
<record id="product_product_tree_view" model="ir.ui.view">
<field name="name">product.product.tree</field>
<field name="model">product.product</field>
<field eval="7" name="priority"/>
<field name="arch" type="xml">
<tree string="Product Variants">
<field name="default_code"/>
<field name="name"/>
<field name="attribute_value_ids" widget="many2many_tags"/>
<field name="lst_price"/>
<field name="price" invisible="not context.get('pricelist',False)"/>
<field name="uom_id"/>
<field name="ean13"/>
<field name="state" invisible="1"/>
<field name="product_tmpl_id" invisible="1"/>
</tree>
</field>
</record>
<record id="product_normal_form_view" model="ir.ui.view">
<field name="name">product.product.form</field>
<field name="model">product.product</field>
<field name="mode">primary</field>
<field eval="7" name="priority"/>
<field name="inherit_id" ref="product.product_template_form_view"/>
<field name="arch" type="xml">
<form position="attributes">
<attribute name="string">Product Variant</attribute>
</form>
<field name="name" position="replace">
<field name="name" attrs="{'invisible': [('id', '!=', False)]}"/>
<field name="product_tmpl_id" class="oe_inline" readonly="1" attrs="{'invisible': [('id', '=', False)]}"/>
</field>
<xpath expr="//div[@class='oe_title']" position="inside">
<field name="attribute_value_ids" widget="many2many_tags"/>
</xpath>
<page name="variants" position="replace">
</page>
</field>
</record>
<record id="product_kanban_view" model="ir.ui.view">
<field name="name">Product Kanban</field>
<field name="model">product.product</field>
<field name="mode">primary</field>
<field name="inherit_id" ref="product.product_template_kanban_view"/>
<field name="arch" type="xml">
<field name="name" position="after">
<field name="attribute_value_ids"/>
</field>
<xpath expr="//img[@class='oe_kanban_image']" position="replace">
<img t-att-src="kanban_image('product.product', 'image_small', record.id.value)" class="oe_kanban_image"/>
</xpath>
</field>
</record>
<!-- -->
<record id="product_normal_action" model="ir.actions.act_window">
<field name="name">Product Variants</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">product.product</field>
<field name="view_mode">tree,form,kanban</field>
<field name="view_id" ref="product_product_tree_view"/>
<field name="view_type">form</field>
<field name="search_view_id" ref="product_search_form_view"/>
<field name="help" type="html">
<p class="oe_view_nocontent_create">
@ -253,8 +313,65 @@
</p>
</field>
</record>
<!-- -->
<record id="product_variant_action" model="ir.actions.act_window">
<field name="name">Product Variants</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">product.product</field>
<field name="view_mode">tree,form,kanban</field>
<field name="view_type">form</field>
<field name="context">{'search_default_product_tmpl_id': [active_id], 'default_product_tmpl_id': active_id}</field>
<field name="search_view_id" ref="product_search_form_view"/>
<field name="help" type="html">
<p class="oe_view_nocontent_create">
Click to define a new product.
</p><p>
You must define a product for everything you buy or sell,
whether it's a physical product, a consumable or service.
</p>
</field>
</record>
<record id="product_template_form_view_variant_button" model="ir.ui.view">
<field name="name">product.template.form</field>
<field name="model">product.template</field>
<field name="inherit_id" ref="product.product_template_form_view"/>
<field name="arch" type="xml">
<field name="attribute_line_ids" position="before">
<div class="oe_right">
<button class="oe_inline oe_stat_button" string="Variant Prices" name="%(product.variants_template_action)d" type="action" icon="fa-strikethrough"/>
<button class="oe_inline oe_stat_button" name="%(product.product_variant_action)d" type="action" icon="fa-sitemap">
<field string="List of Variants" name="product_variant_count" widget="statinfo" />
</button>
</div>
</field>
</field>
</record>
<record id="product_template_kanban_view_variant_button" model="ir.ui.view">
<field name="name">product.template.form</field>
<field name="model">product.template</field>
<field name="inherit_id" ref="product.product_template_kanban_view"/>
<field name="arch" type="xml">
<field name="lst_price" position="after">
<field name="is_product_variant"/>
<field name="product_variant_count"/>
<field name="product_variant_ids"/>
</field>
<h4 position="after">
<a name="%(product.product_variant_action)d" type="action" t-if="!record.is_product_variant.raw_value &amp; record.product_variant_count.raw_value&gt;1">
<t t-esc="record.product_variant_count.value"/> Variants
</a>
</h4>
</field>
</record>
<!-- -->
<record id="product_normal_action_sell" model="ir.actions.act_window">
<field name="name">Products</field>
<field name="name">Product Variants</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">product.product</field>
<field name="view_mode">kanban,tree,form</field>
@ -277,53 +394,7 @@
</field>
</record>
<record id="open_view_product_tree1" model="ir.actions.act_window.view">
<field name="sequence" eval="2"/>
<field name="view_mode">tree</field>
<field name="view_id" ref="product_product_tree_view"/>
<field name="act_window_id" ref="product_normal_action_sell"/>
</record>
<record id="open_view_product_form1" model="ir.actions.act_window.view">
<field name="sequence" eval="3"/>
<field name="view_mode">form</field>
<field name="view_id" ref="product_normal_form_view"/>
<field name="act_window_id" ref="product_normal_action_sell"/>
</record>
<record id="open_view_product_kanban1" model="ir.actions.act_window.view">
<field name="sequence" eval="1"/>
<field name="view_mode">kanban</field>
<field name="view_id" ref="product_kanban_view"/>
<field name="act_window_id" ref="product_normal_action_sell"/>
</record>
<menuitem id="base.menu_product" name="Products" parent="base.menu_base_partner" sequence="9"/>
<menuitem id="product.menu_products" action="product.product_normal_action_sell" parent="base.menu_product" sequence="1"/>
<record id="product_normal_action_puchased" model="ir.actions.act_window">
<field name="name">Products</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">product.product</field>
<field name="view_type">form</field>
<field name="view_mode">kanban,tree,form</field>
<field name="context">{"search_default_filter_to_purchase":1}</field>
<field name="view_id" ref="product_kanban_view"/>
<field name="search_view_id" ref="product_search_form_view"/>
<field name="help" type="html">
<p class="oe_view_nocontent_create">
Click to define a new product.
</p><p>
You must define a product for everything you purchase, whether
it's a physical product, a consumable or services you buy to
subcontractants.
</p><p>
The product form contains detailed information to improve the
purchase process: prices, procurement logistics, accounting data,
available suppliers, etc.
</p>
</field>
</record>
<menuitem id="product.menu_products" action="product.product_normal_action_sell" parent="base.menu_product" groups="base.group_no_one" sequence="10"/>
<record id="product_category_search_view" model="ir.ui.view">
<field name="name">product.category.search</field>
@ -412,7 +483,7 @@
<record id="product_normal_action_tree" model="ir.actions.act_window">
<field name="name">Products</field>
<field name="name">Product Variants</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">product.product</field>
<field name="view_type">form</field>
@ -426,60 +497,6 @@
<field eval="'ir.actions.act_window,%d'%product_normal_action_tree" name="value"/>
</record>
<!-- Product Public Categories -->
<record id="product_public_category_form_view" model="ir.ui.view">
<field name="name">product.public.category.form</field>
<field name="model">product.public.category</field>
<field name="arch" type="xml">
<form string="Pos/Public Categories" version="7.0">
<sheet>
<field name="image_medium" widget='image' class="oe_avatar oe_right"/>
<div class="oe_left">
<group>
<field name="name"/>
<field name="parent_id"/>
<field name="sequence"/>
</group>
</div>
</sheet>
</form>
</field>
</record>
<record id="product_public_category_tree_view" model="ir.ui.view">
<field name="name">product.public.category.tree</field>
<field name="model">product.public.category</field>
<field name="field_parent" eval="False"/>
<field name="arch" type="xml">
<tree string="Product Product Categories">
<field name="sequence" invisible="1"/>
<field name="complete_name"/>
</tree>
</field>
</record>
<record id="product_public_category_action" model="ir.actions.act_window">
<field name="name">Pos/Public Product Categories</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">product.public.category</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="view_id" eval="False"/>
<field name="help" type="html">
<p class="oe_view_nocontent_create">
Click to define a new category.
</p><p>
Categories are used to browse your products through the
touchscreen interface.
</p><p>
If you put a photo on the category, the layout of the
touchscreen interface will automatically. We suggest not to put
a photo on categories for small (1024x768) screens.
</p>
</field>
</record>
<menuitem action="product_public_category_action" id="menu_product_public_category" parent="prod_config_main" sequence="10" />
<!-- END -->
<!-- Unit of Measure -->
<record id="product_uom_tree_view" model="ir.ui.view">
@ -704,261 +721,5 @@
</field>
</record>
<!-- Variants -->
<record id="product_variant_search_form_view" model="ir.ui.view">
<field name="name">product.variant.search.form</field>
<field name="model">product.product</field>
<field name="arch" type="xml">
<search string="Product Variant">
<field name="product_tmpl_id"/>
<field name="name" string="Product" filter_domain="['|','|',('name','ilike',self),('default_code','ilike',self),('variants','ilike',self)]"/>
<group expand='0' string='Group by...'>
<filter string='Template' name="template_id" domain="[]" context="{'group_by' : 'product_tmpl_id'}"/>
</group>
</search>
</field>
</record>
<record id="product_variant_form_view" model="ir.ui.view">
<field name="name">product.variant.form</field>
<field name="model">product.product</field>
<field name="arch" type="xml">
<form string="Product Variant" version="7.0">
<group col="4">
<field name="product_tmpl_id"/>
<field name="active"/>
<field name="variants" required="1"/>
<field name="default_code"/>
<field name="price_margin"/>
<field name="price_extra"/>
</group>
</form>
</field>
</record>
<record id="product_variant_tree_view" model="ir.ui.view">
<field name="name">product.variant.tree</field>
<field name="model">product.product</field>
<field name="arch" type="xml">
<tree string="Product Variant">
<field name="product_tmpl_id"/>
<field name="variants"/>
<field name="default_code"/>
<field name="price_margin"/>
<field name="price_extra"/>
<field name="company_id" invisible="1"/>
<field name="type" invisible="1"/>
<field name="uom_id" invisible="1"/>
<field name="categ_id" invisible="1"/>
</tree>
</field>
</record>
<record id="product_variant_action" model="ir.actions.act_window">
<field name="name">Product Variants</field>
<field name="type">ir.actions.act_window</field>
<field name="domain">[('variants','!=', '')]</field>
<field name="res_model">product.product</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form,kanban</field>
<field name="view_id" ref="product_variant_tree_view"/>
<field name="search_view_id" ref="product_variant_search_form_view"/>
<field name="help" type="html">
<p class="oe_view_nocontent_create">
Click to define a new variant of product.
</p>
</field>
</record>
<record id="tree_view_product_variant" model="ir.actions.act_window.view">
<field name="sequence" eval="10"/>
<field name="view_mode">tree</field>
<field name="view_id" ref="product_variant_tree_view"/>
<field name="act_window_id" ref="product_variant_action"/>
</record>
<record id="form_view_product_variant" model="ir.actions.act_window.view">
<field name="sequence" eval="20"/>
<field name="view_mode">form</field>
<field name="view_id" ref="product_variant_form_view"/>
<field name="act_window_id" ref="product_variant_action"/>
</record>
<menuitem action="product.product_variant_action" id="product.menu_variant_product" parent="prod_config_main" sequence="4" groups="product.group_product_variant"/>
<!-- templates -->
<record id="product_template_search_view" model="ir.ui.view">
<field name="name">product.template.search</field>
<field name="model">product.template</field>
<field name="arch" type="xml">
<search string="Product Template">
<field name="name" string="Product"/>
<filter string="Services" icon="terp-accessories-archiver" domain="[('type','=','service')]"/>
<filter string="Consumable" name="consumable" icon="terp-accessories-archiver" domain="[('type','=','consu')]" help="Consumable products"/>
<separator/>
<filter string="Can be Sold" name="filter_to_sell" icon="terp-accessories-archiver-minus" domain="[('sale_ok','=',1)]"/>
<field name="categ_id"/>
<group expand='0' string='Group by...'>
<filter string='Category' icon="terp-stock_symbol-selection" domain="[]" context="{'group_by' : 'categ_id'}"/>
<filter string='Default Unit of Measure' icon="terp-mrp" domain="[]" context="{'group_by' : 'uom_id'}"/>
<filter string='Type' icon="terp-stock_symbol-selection" domain="[]" context="{'group_by' : 'type'}"/>
</group>
</search>
</field>
</record>
<record id="product_template_tree_view" model="ir.ui.view">
<field name="name">product.template.product.tree</field>
<field name="model">product.template</field>
<field name="arch" type="xml">
<tree string="Product Template">
<field name="name"/>
<field name="categ_id"/>
<field name="type"/>
<field name="state"/>
<field name="uom_id" invisible="1"/>
</tree>
</field>
</record>
<record id="product_template_form_view" model="ir.ui.view">
<field name="name">product.template.product.form</field>
<field name="model">product.template</field>
<field name="arch" type="xml">
<form string="Product Template" version="7.0">
<sheet>
<field name="image_medium" widget="image" class="oe_avatar oe_left"/>
<div class="oe_title">
<div class="oe_edit_only">
<label for="name" string="Product Name"/>
</div>
<h1>
<field name="name"/>
</h1>
<label for="categ_id" class="oe_edit_only"/>
<h2><field name="categ_id"/></h2>
<label for="public_categ_id" class="oe_edit_only"/>
<h3><field name="public_categ_id"/></h3>
<div name="options" groups="base.group_user">
<field name="sale_ok"/>
<label for="sale_ok"/>
</div>
</div>
<notebook>
<page string="Information">
<group colspan="4">
<group>
<field name="type"/>
<field name="uom_id" on_change="onchange_uom(uom_id,uom_po_id)" groups="product.group_uom"/>
<field name="list_price"/>
</group>
<group>
<field name="company_id" groups="base.group_multi_company" widget="selection"/>
</group>
</group>
<group colspan="4" string="Product Variants" groups="product.group_product_variant">
<field colspan="4" name="product_variant_ids" nolabel="1" >
<tree string="Product Variants" editable="bottom">
<field name="variants" required="1"/>
<field name="price_margin" string="Variant Price Margin"/>
<field name="price_extra"/>
<field name="lst_price" string="Sale Price" readonly="1"/>
</tree>
</field>
</group>
<field name="description" placeholder="describe the product characteristics..."/>
</page>
<page string="Procurements" groups="base.group_user">
<group name="procurement">
<group name="general">
<field name="standard_price" attrs="{'readonly': [('is_only_child', '=', False)]}"/>
</group>
<group name="procurement_uom" groups="product.group_uom" string="Purchase">
<field name="uom_po_id"/>
</group>
</group>
<separator string="Suppliers"/>
<field name="seller_ids"/>
<separator string="Description for Suppliers"/>
<field name="description_purchase" placeholder="This note will be displayed on requests for quotation..."/>
</page>
<page string="Inventory">
<group name="inventory">
<group name="status" string="Status">
<field name="state"/>
<field name="product_manager"/>
</group>
<group name ="weight" string="Weights">
<field digits="(14, 3)" name="volume" attrs="{'readonly':[('type','=','service')]}"/>
<field digits="(14, 3)" name="weight" attrs="{'readonly':[('type','=','service')]}"/>
<field digits="(14, 3)" name="weight_net" attrs="{'readonly':[('type','=','service')]}"/>
</group>
</group>
</page>
<page string="Sales" attrs="{'invisible':[('sale_ok','=',False)]}">
<group name="sale">
<group name="sale_condition" string="Sale Conditions">
<label for="warranty"/>
<div>
<field name="warranty" class="oe_inline" style="vertical-align:baseline"/> months
</div>
</group>
<group groups="product.group_uos" string="Unit of Measure">
<field name="uos_id"/>
<field name="uos_coeff"/>
<field name="mes_type"/>
</group>
</group>
<separator string="Description for Quotations"/>
<field name="description_sale" placeholder="note to be displayed on quotations..."/>
</page>
</notebook>
</sheet>
<div class="oe_chatter">
<field name="message_follower_ids" widget="mail_followers"/>
<field name="message_ids" widget="mail_thread"/>
</div>
</form>
</field>
</record>
<record model="ir.ui.view" id="product_template_kanban_view">
<field name="name">Product Template Kanban</field>
<field name="model">product.template</field>
<field name="arch" type="xml">
<kanban>
<field name="image_small"/>
<field name="list_price"/>
<templates>
<t t-name="kanban-box">
<div class="oe_kanban_vignette oe_semantic_html_override">
<a type="open"><img t-att-src="kanban_image('product.template', 'image_small', record.id.value)" class="oe_kanban_image"/></a>
<div class="oe_kanban_details">
<h4>
<a type="open">
<field name="name"/>
</a>
</h4>
<div name="tags"/>
<ul>
<li>Price: <field name="list_price"></field></li>
</ul>
</div>
</div>
</t>
</templates>
</kanban>
</field>
</record>
<record id="product_template_action" model="ir.actions.act_window">
<field name="name">Templates</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">product.template</field>
<field name="view_mode">kanban,tree,form</field>
<field name="view_type">form</field>
<field name="view_id" ref="product_template_kanban_view"/>
</record>
<menuitem action="product_template_action"
id="menu_product_template_action"
parent="base.menu_product" sequence="20"
groups="product.group_product_variant"/>
</data>
</openerp>

View File

@ -3,7 +3,7 @@
<lot-line type="fields" name="id">
<code type="field" name="code"/>
<product type="field" name="name"/>
<variant type="field" name="variants"/>
<variant type="field" name="attribute_value_ids"/>
<price type="field" name="list_price"/>
<ean13 type="field" name="ean13"/>
<currency type="field" name="company_id.currency_id.name"/>

View File

@ -19,5 +19,11 @@ access_product_price_history_employee,prices.history employee,model_product_pric
access_product_template_sale_manager,product.template salemanager,model_product_template,base.group_sale_manager,1,1,1,1
access_product_product_sale_manager,product.product salemanager,model_product_product,base.group_sale_manager,1,1,1,1
access_product_category_sale_manager,product.category salemanager,product.model_product_category,base.group_sale_manager,1,1,1,1
access_product_category_pos_manager,product.public.category manager,model_product_public_category,base.group_sale_manager,1,1,1,1
access_product_category_pos_user,product.public.category user,model_product_public_category,base.group_user,1,0,0,0
access_product_attribute,product.attribute,model_product_attribute,base.group_user,1,0,0,0
access_product_attribute_value,product.attribute value,model_product_attribute_value,base.group_user,1,0,0,0
access_product_attribute_price,product.attribute price,model_product_attribute_price,base.group_user,1,0,0,0
access_product_attribute_line,product.attribute line,model_product_attribute_line,base.group_user,1,0,0,0
access_product_attribute_sale_manager,product.attribute manager,model_product_attribute,base.group_sale_manager,1,1,1,1
access_product_attribute_value_sale_manager,product.attribute manager value,model_product_attribute_value,base.group_sale_manager,1,1,1,1
access_product_attribute_price_sale_manager,product.attribute manager price,model_product_attribute_price,base.group_sale_manager,1,1,1,1
access_product_attribute_line_sale_manager,product.attribute manager line,model_product_attribute_line,base.group_sale_manager,1,1,1,1

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
19 access_product_template_sale_manager product.template salemanager model_product_template base.group_sale_manager 1 1 1 1
20 access_product_product_sale_manager product.product salemanager model_product_product base.group_sale_manager 1 1 1 1
21 access_product_category_sale_manager product.category salemanager product.model_product_category base.group_sale_manager 1 1 1 1
22 access_product_category_pos_manager access_product_attribute product.public.category manager product.attribute model_product_public_category model_product_attribute base.group_sale_manager base.group_user 1 1 0 1 0 1 0
23 access_product_category_pos_user access_product_attribute_value product.public.category user product.attribute value model_product_public_category model_product_attribute_value base.group_user 1 0 0 0
24 access_product_attribute_price product.attribute price model_product_attribute_price base.group_user 1 0 0 0
25 access_product_attribute_line product.attribute line model_product_attribute_line base.group_user 1 0 0 0
26 access_product_attribute_sale_manager product.attribute manager model_product_attribute base.group_sale_manager 1 1 1 1
27 access_product_attribute_value_sale_manager product.attribute manager value model_product_attribute_value base.group_sale_manager 1 1 1 1
28 access_product_attribute_price_sale_manager product.attribute manager price model_product_attribute_price base.group_sale_manager 1 1 1 1
29 access_product_attribute_line_sale_manager product.attribute manager line model_product_attribute_line base.group_sale_manager 1 1 1 1

View File

@ -1,21 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data noupdate="0">
<!-- Change name of group for 7.0 users since product variant module doesn't exist anymore
DO NOT CHANGE IT IN TRUNK -->
<record id="group_product_variant" model="res.groups">
<field name="name">Manage Product Variants</field>
<field name="category_id" ref="base.module_category_hidden"/>
</record>
<record id="group_product_mono" model="res.groups">
<field name="name">Do Not Manage Product Variants</field>
<field name="category_id" ref="base.module_category_hidden"/>
</record>
<record id="base.group_user" model="res.groups">
<field name="implied_ids" eval="[(4, ref('product.group_product_mono'))]"/>
</record>
<record id="group_sale_pricelist" model="res.groups">
<field name="name">Sales Pricelists</field>

View File

@ -6,19 +6,19 @@
!python {model: product.product}: |
context.update({'pricelist': ref("customer_pricelist"), 'quantity':1})
product = self.browse(cr, uid, ref("product_product_4"), context=context)
assert product.price == (product.lst_price-product.lst_price*(0.10)), "Wrong sale price."
assert product.price == (product.lst_price-product.lst_price*(0.10)), "Wrong sale price: Assemble Computer."
-
I check sale price of Laptop.
-
!python {model: product.product}: |
product = self.browse(cr, uid, ref("product_product_25"), context=context)
assert product.price == product.lst_price + 1, "Wrong sale price."
assert product.price == product.lst_price + 1, "Wrong sale price: Laptop."
-
I check sale price of IT component.
-
!python {model: product.product}: |
product = self.browse(cr, uid, ref("product_product_7"), context=context)
assert product.price == product.lst_price, "Wrong sale price."
assert product.price == product.lst_price, "Wrong sale price: IT component."
-
I check sale price of IT component if more than 3 Unit.
@ -26,14 +26,14 @@
!python {model: product.product}: |
context.update({'quantity':5})
product = self.browse(cr, uid, ref("product_product_26"), context=context)
assert product.price == product.lst_price-product.lst_price*(0.05), "Wrong sale price."
assert product.price == product.lst_price-product.lst_price*(0.05), "Wrong sale price: IT component if more than 3 Unit."
-
I check sale price of LCD Monitor.
-
!python {model: product.product}: |
context.update({'quantity':1})
product = self.browse(cr, uid, ref("product_product_6"), context=context)
assert product.price == product.lst_price, "Wrong sale price."
assert product.price == product.lst_price, "Wrong sale price: LCD Monitor."
-
I check sale price of LCD Monitor on end of year.
@ -41,7 +41,7 @@
!python {model: product.product}: |
context.update({'quantity':1, 'date': '2011-12-31'})
product = self.browse(cr, uid, ref("product_product_6"), context=context)
assert product.price == product.lst_price-product.lst_price*(0.30), "Wrong sale price."
assert product.price == product.lst_price-product.lst_price*(0.30), "Wrong sale price: LCD Monitor on end of year."
-
I check cost price of LCD Monitor.
@ -49,14 +49,14 @@
!python {model: product.product}: |
context.update({'quantity':1, 'date': False, 'partner': ref('base.res_partner_4'), 'pricelist': ref("supplier_pricelist")})
product = self.browse(cr, uid, ref("product_product_6"), context=context)
assert product.price == 792, "wrong cost price."
assert product.price == 792, "wrong cost price: LCD Monitor."
-
I check cost price of LCD Monitor if more than 3 Unit.
-
!python {model: product.product}: |
context.update({'quantity':3})
product = self.browse(cr, uid, ref("product_product_6"), context=context)
assert product.price == 787, "wrong cost price."
assert product.price == 787, "wrong cost price: LCD Monitor if more than 3 Unit."
-
I print the sale prices report.

View File

@ -1,10 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="product_normal_form_view_template" model="ir.ui.view">
<field name="name">product.normal.procurement.locations.inherit</field>
<field name="model">product.product</field>
<field name="inherit_id" ref="product.product_normal_form_view"/>
<record id="product_template_form_view" model="ir.ui.view">
<field name="name">product.template.form.inherit</field>
<field name="model">product.template</field>
<field name="inherit_id" ref="product.product_template_form_view"/>
<field name="arch" type="xml">
<field name="company_id" position="after">
<field name="email_template_id"
@ -15,7 +15,7 @@
'default_subject': name,
'default_name': name,
}"
attrs="{'invisible': [('is_only_child', '=', False)]}"
attrs="{'invisible': [('is_product_variant', '=', False)]}"
/>
</field>
</field>

View File

@ -98,7 +98,7 @@ class stock_quant(osv.osv):
class product_product(osv.osv):
_inherit = 'product.product'
_inherit = 'product.template'
_columns = {
'life_time': fields.integer('Product Life Time',
help='When a new a Serial Number is issued, this is the number of days before the goods may become dangerous and must not be consumed.'),

View File

@ -23,10 +23,10 @@
<record model="ir.ui.view" id="view_product_form_expiry">
<field name="name">product.normal.form</field>
<field name="model">product.product</field>
<field name="inherit_id" ref="stock.view_normal_procurement_locations_form" />
<field name="model">product.template</field>
<field name="inherit_id" ref="product.product_template_form_view" />
<field name="arch" type="xml">
<group name="Weights" position="after">
<group name="weight" position="after">
<group string="Dates">
<field name="life_time" />
<field name="use_time" />

View File

@ -36,7 +36,7 @@ class product_product(osv.osv):
testdict = {}
for prod_id in ids:
bom_obj = self.pool.get('mrp.bom')
bom_ids = bom_obj.search(cr, uid, [('bom_id', '=', False), ('product_id','=', prod_id), ('bom_lines', '!=', False)], context=context)
bom_ids = bom_obj.search(cr, uid, [('product_id','=', prod_id), ('bom_line_ids', '!=', False)], context=context)
if bom_ids:
bom_id = bom_ids[0]
# In recursive mode, it will first compute the prices of child boms
@ -63,9 +63,9 @@ class product_product(osv.osv):
context={}
price = 0
uom_obj = self.pool.get("product.uom")
if bom.bom_lines:
for sbom in bom.bom_lines:
my_qty = sbom.bom_lines and 1.0 or sbom.product_qty
if bom.bom_line_ids:
for sbom in bom.bom_line_ids:
my_qty = sbom.bom_line_ids and 1.0 or sbom.product_qty
price += uom_obj._compute_price(cr, uid, sbom.product_id.uom_id.id, sbom.product_id.standard_price, sbom.product_uom.id) * my_qty
if bom.routing_id:
@ -98,7 +98,7 @@ class product_bom(osv.osv):
_inherit = 'mrp.bom'
_columns = {
'standard_price': fields.related('product_id','standard_price',type="float",relation="product.product",string="Standard Price",store=False)
'standard_price': fields.related('product_tmpl_id','standard_price',type="float",relation="product.product",string="Standard Price",store=False)
}
product_bom()

View File

@ -1,46 +0,0 @@
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
{
'name': 'Products Manufacturers',
'version': '1.0',
'author': 'OpenERP SA',
'category': 'Purchase Management',
'depends': ['stock'],
'demo': [],
'description': """
A module that adds manufacturers and attributes on the product form.
====================================================================
You can now define the following for a product:
-----------------------------------------------
* Manufacturer
* Manufacturer Product Name
* Manufacturer Product Code
* Product Attributes
""",
'data': [
'security/ir.model.access.csv',
'product_manufacturer_view.xml'
],
'auto_install': False,
'installable': True,
'images': ['images/products_manufacturer.jpeg'],
}
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -1,75 +0,0 @@
# Arabic translation for openobject-addons
# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012
# This file is distributed under the same license as the openobject-addons package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2012.
#
msgid ""
msgstr ""
"Project-Id-Version: openobject-addons\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2012-12-21 17:06+0000\n"
"PO-Revision-Date: 2012-01-12 22:07+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Arabic <ar@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2014-04-22 07:26+0000\n"
"X-Generator: Launchpad (build 16985)\n"
#. module: product_manufacturer
#: field:product.product,manufacturer_pref:0
msgid "Manufacturer Product Code"
msgstr "كود تصنيع المنتج"
#. module: product_manufacturer
#: model:ir.model,name:product_manufacturer.model_product_product
#: field:product.manufacturer.attribute,product_id:0
msgid "Product"
msgstr "المنتج"
#. module: product_manufacturer
#: view:product.manufacturer.attribute:0
msgid "Product Template Name"
msgstr "اسم قالب المنتج"
#. module: product_manufacturer
#: model:ir.model,name:product_manufacturer.model_product_manufacturer_attribute
msgid "Product attributes"
msgstr "سمات المنتج"
#. module: product_manufacturer
#: view:product.manufacturer.attribute:0
#: view:product.product:0
msgid "Product Attributes"
msgstr "سمات المنتج"
#. module: product_manufacturer
#: field:product.manufacturer.attribute,name:0
msgid "Attribute"
msgstr "الخاصية"
#. module: product_manufacturer
#: field:product.manufacturer.attribute,value:0
msgid "Value"
msgstr "قيمة"
#. module: product_manufacturer
#: view:product.product:0
#: field:product.product,attribute_ids:0
msgid "Attributes"
msgstr "صفات"
#. module: product_manufacturer
#: field:product.product,manufacturer_pname:0
msgid "Manufacturer Product Name"
msgstr "اسم مصنع المنتج"
#. module: product_manufacturer
#: view:product.product:0
#: field:product.product,manufacturer:0
msgid "Manufacturer"
msgstr "مصنّع"
#~ msgid "Error: Invalid ean code"
#~ msgstr "خطأ: كود إين غير صالح"

View File

@ -1,81 +0,0 @@
# Bulgarian translation for openobject-addons
# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011
# This file is distributed under the same license as the openobject-addons package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2011.
#
msgid ""
msgstr ""
"Project-Id-Version: openobject-addons\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2012-12-21 17:06+0000\n"
"PO-Revision-Date: 2011-02-10 23:42+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Bulgarian <bg@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2014-04-22 07:26+0000\n"
"X-Generator: Launchpad (build 16985)\n"
#. module: product_manufacturer
#: field:product.product,manufacturer_pref:0
msgid "Manufacturer Product Code"
msgstr "Код на производител"
#. module: product_manufacturer
#: model:ir.model,name:product_manufacturer.model_product_product
#: field:product.manufacturer.attribute,product_id:0
msgid "Product"
msgstr "Продукт"
#. module: product_manufacturer
#: view:product.manufacturer.attribute:0
msgid "Product Template Name"
msgstr "Име на шаблона на продукта"
#. module: product_manufacturer
#: model:ir.model,name:product_manufacturer.model_product_manufacturer_attribute
msgid "Product attributes"
msgstr "Атрибути на продукта"
#. module: product_manufacturer
#: view:product.manufacturer.attribute:0
#: view:product.product:0
msgid "Product Attributes"
msgstr "Атрибути на продукта"
#. module: product_manufacturer
#: field:product.manufacturer.attribute,name:0
msgid "Attribute"
msgstr "Атрибут"
#. module: product_manufacturer
#: field:product.manufacturer.attribute,value:0
msgid "Value"
msgstr "Стойност"
#. module: product_manufacturer
#: view:product.product:0
#: field:product.product,attribute_ids:0
msgid "Attributes"
msgstr "Атрибути"
#. module: product_manufacturer
#: field:product.product,manufacturer_pname:0
msgid "Manufacturer Product Name"
msgstr "Име на производител"
#. module: product_manufacturer
#: view:product.product:0
#: field:product.product,manufacturer:0
msgid "Manufacturer"
msgstr "Производител"
#~ msgid "Error: Invalid ean code"
#~ msgstr "Грешка: Невалиден европейски баркод"
#~ msgid "A module that add manufacturers and attributes on the product form"
#~ msgstr "Модул, който добавя производители и атрибути във формата на продукта"
#~ msgid "Products Attributes & Manufacturers"
#~ msgstr "Продуктови атрибути и производители"

View File

@ -1,72 +0,0 @@
# Bosnian translation for openobject-addons
# Copyright (c) 2013 Rosetta Contributors and Canonical Ltd 2013
# This file is distributed under the same license as the openobject-addons package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2013.
#
msgid ""
msgstr ""
"Project-Id-Version: openobject-addons\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2012-12-21 17:06+0000\n"
"PO-Revision-Date: 2013-10-30 00:09+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Bosnian <bs@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2014-04-22 07:26+0000\n"
"X-Generator: Launchpad (build 16985)\n"
#. module: product_manufacturer
#: field:product.product,manufacturer_pref:0
msgid "Manufacturer Product Code"
msgstr "Proizvođačka šifra artikla"
#. module: product_manufacturer
#: model:ir.model,name:product_manufacturer.model_product_product
#: field:product.manufacturer.attribute,product_id:0
msgid "Product"
msgstr "Proizvod"
#. module: product_manufacturer
#: view:product.manufacturer.attribute:0
msgid "Product Template Name"
msgstr "Naziv predloška proizvoda"
#. module: product_manufacturer
#: model:ir.model,name:product_manufacturer.model_product_manufacturer_attribute
msgid "Product attributes"
msgstr "Atributi proizvoda"
#. module: product_manufacturer
#: view:product.manufacturer.attribute:0
#: view:product.product:0
msgid "Product Attributes"
msgstr "Atributi proizvoda"
#. module: product_manufacturer
#: field:product.manufacturer.attribute,name:0
msgid "Attribute"
msgstr "Atribut"
#. module: product_manufacturer
#: field:product.manufacturer.attribute,value:0
msgid "Value"
msgstr "Vrijednost"
#. module: product_manufacturer
#: view:product.product:0
#: field:product.product,attribute_ids:0
msgid "Attributes"
msgstr "Atributi"
#. module: product_manufacturer
#: field:product.product,manufacturer_pname:0
msgid "Manufacturer Product Name"
msgstr "Proizvođački naziv proizvoda"
#. module: product_manufacturer
#: view:product.product:0
#: field:product.product,manufacturer:0
msgid "Manufacturer"
msgstr "Proizvođač"

View File

@ -1,82 +0,0 @@
# Catalan translation for openobject-addons
# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011
# This file is distributed under the same license as the openobject-addons package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2011.
#
msgid ""
msgstr ""
"Project-Id-Version: openobject-addons\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2012-12-21 17:06+0000\n"
"PO-Revision-Date: 2011-02-12 21:08+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Catalan <ca@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2014-04-22 07:26+0000\n"
"X-Generator: Launchpad (build 16985)\n"
#. module: product_manufacturer
#: field:product.product,manufacturer_pref:0
msgid "Manufacturer Product Code"
msgstr "Codi de producte del fabricant"
#. module: product_manufacturer
#: model:ir.model,name:product_manufacturer.model_product_product
#: field:product.manufacturer.attribute,product_id:0
msgid "Product"
msgstr "Producte"
#. module: product_manufacturer
#: view:product.manufacturer.attribute:0
msgid "Product Template Name"
msgstr "Nom de plantilla de producte"
#. module: product_manufacturer
#: model:ir.model,name:product_manufacturer.model_product_manufacturer_attribute
msgid "Product attributes"
msgstr "Atributs de producte"
#. module: product_manufacturer
#: view:product.manufacturer.attribute:0
#: view:product.product:0
msgid "Product Attributes"
msgstr "Atributs del producte"
#. module: product_manufacturer
#: field:product.manufacturer.attribute,name:0
msgid "Attribute"
msgstr "Atribut"
#. module: product_manufacturer
#: field:product.manufacturer.attribute,value:0
msgid "Value"
msgstr "Valor"
#. module: product_manufacturer
#: view:product.product:0
#: field:product.product,attribute_ids:0
msgid "Attributes"
msgstr "Atributs"
#. module: product_manufacturer
#: field:product.product,manufacturer_pname:0
msgid "Manufacturer Product Name"
msgstr "Nom del producte del fabricant"
#. module: product_manufacturer
#: view:product.product:0
#: field:product.product,manufacturer:0
msgid "Manufacturer"
msgstr "Fabricant"
#~ msgid "A module that add manufacturers and attributes on the product form"
#~ msgstr ""
#~ "Un mòdul que afegeix fabricants i atributs en el formulari de producte"
#~ msgid "Error: Invalid ean code"
#~ msgstr "Error: Codi EAN no vàlid"
#~ msgid "Products Attributes & Manufacturers"
#~ msgstr "Fabricants i atributs dels productes"

View File

@ -1,72 +0,0 @@
# Czech translation for openobject-addons
# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012
# This file is distributed under the same license as the openobject-addons package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2012.
#
msgid ""
msgstr ""
"Project-Id-Version: openobject-addons\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2012-12-21 17:06+0000\n"
"PO-Revision-Date: 2012-12-31 10:46+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Czech <cs@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2014-04-22 07:26+0000\n"
"X-Generator: Launchpad (build 16985)\n"
#. module: product_manufacturer
#: field:product.product,manufacturer_pref:0
msgid "Manufacturer Product Code"
msgstr "Kód výrobku od výrobce"
#. module: product_manufacturer
#: model:ir.model,name:product_manufacturer.model_product_product
#: field:product.manufacturer.attribute,product_id:0
msgid "Product"
msgstr "Výrobek"
#. module: product_manufacturer
#: view:product.manufacturer.attribute:0
msgid "Product Template Name"
msgstr "Název šalony výrobku"
#. module: product_manufacturer
#: model:ir.model,name:product_manufacturer.model_product_manufacturer_attribute
msgid "Product attributes"
msgstr "Vlastnosti výrobku"
#. module: product_manufacturer
#: view:product.manufacturer.attribute:0
#: view:product.product:0
msgid "Product Attributes"
msgstr "Vlastnosti výrobku"
#. module: product_manufacturer
#: field:product.manufacturer.attribute,name:0
msgid "Attribute"
msgstr "Vlastnost"
#. module: product_manufacturer
#: field:product.manufacturer.attribute,value:0
msgid "Value"
msgstr "Hodnota"
#. module: product_manufacturer
#: view:product.product:0
#: field:product.product,attribute_ids:0
msgid "Attributes"
msgstr "Vlastnosti"
#. module: product_manufacturer
#: field:product.product,manufacturer_pname:0
msgid "Manufacturer Product Name"
msgstr "Název výrobku od výrobce"
#. module: product_manufacturer
#: view:product.product:0
#: field:product.product,manufacturer:0
msgid "Manufacturer"
msgstr "Výrobce"

View File

@ -1,72 +0,0 @@
# Danish translation for openobject-addons
# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012
# This file is distributed under the same license as the openobject-addons package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2012.
#
msgid ""
msgstr ""
"Project-Id-Version: openobject-addons\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2012-12-21 17:06+0000\n"
"PO-Revision-Date: 2012-01-27 06:25+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Danish <da@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2014-04-22 07:26+0000\n"
"X-Generator: Launchpad (build 16985)\n"
#. module: product_manufacturer
#: field:product.product,manufacturer_pref:0
msgid "Manufacturer Product Code"
msgstr "Producent varenummer"
#. module: product_manufacturer
#: model:ir.model,name:product_manufacturer.model_product_product
#: field:product.manufacturer.attribute,product_id:0
msgid "Product"
msgstr "Vare"
#. module: product_manufacturer
#: view:product.manufacturer.attribute:0
msgid "Product Template Name"
msgstr "Vare skabelon navn"
#. module: product_manufacturer
#: model:ir.model,name:product_manufacturer.model_product_manufacturer_attribute
msgid "Product attributes"
msgstr "Vare egenskaber"
#. module: product_manufacturer
#: view:product.manufacturer.attribute:0
#: view:product.product:0
msgid "Product Attributes"
msgstr "Vare Egenskaber"
#. module: product_manufacturer
#: field:product.manufacturer.attribute,name:0
msgid "Attribute"
msgstr "Egenskab"
#. module: product_manufacturer
#: field:product.manufacturer.attribute,value:0
msgid "Value"
msgstr "Værdi"
#. module: product_manufacturer
#: view:product.product:0
#: field:product.product,attribute_ids:0
msgid "Attributes"
msgstr "Egenskaber"
#. module: product_manufacturer
#: field:product.product,manufacturer_pname:0
msgid "Manufacturer Product Name"
msgstr "Producent Vare Navn"
#. module: product_manufacturer
#: view:product.product:0
#: field:product.product,manufacturer:0
msgid "Manufacturer"
msgstr "Producent"

View File

@ -1,83 +0,0 @@
# German translation for openobject-addons
# Copyright (c) 2010 Rosetta Contributors and Canonical Ltd 2010
# This file is distributed under the same license as the openobject-addons package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2010.
#
msgid ""
msgstr ""
"Project-Id-Version: openobject-addons\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2012-12-21 17:06+0000\n"
"PO-Revision-Date: 2011-02-12 20:19+0000\n"
"Last-Translator: Steffi Frank (Bremskerl, DE) <Unknown>\n"
"Language-Team: German <de@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2014-04-22 07:26+0000\n"
"X-Generator: Launchpad (build 16985)\n"
#. module: product_manufacturer
#: field:product.product,manufacturer_pref:0
msgid "Manufacturer Product Code"
msgstr "Hersteller-Artikelnummer"
#. module: product_manufacturer
#: model:ir.model,name:product_manufacturer.model_product_product
#: field:product.manufacturer.attribute,product_id:0
msgid "Product"
msgstr "Produkt"
#. module: product_manufacturer
#: view:product.manufacturer.attribute:0
msgid "Product Template Name"
msgstr "Produkt Vorlagenname"
#. module: product_manufacturer
#: model:ir.model,name:product_manufacturer.model_product_manufacturer_attribute
msgid "Product attributes"
msgstr "Produkteigenschaften"
#. module: product_manufacturer
#: view:product.manufacturer.attribute:0
#: view:product.product:0
msgid "Product Attributes"
msgstr "Produkteigenschaften"
#. module: product_manufacturer
#: field:product.manufacturer.attribute,name:0
msgid "Attribute"
msgstr "Eigenschaft"
#. module: product_manufacturer
#: field:product.manufacturer.attribute,value:0
msgid "Value"
msgstr "Wert"
#. module: product_manufacturer
#: view:product.product:0
#: field:product.product,attribute_ids:0
msgid "Attributes"
msgstr "Eigenschaften"
#. module: product_manufacturer
#: field:product.product,manufacturer_pname:0
msgid "Manufacturer Product Name"
msgstr "Hersteller-Produktname"
#. module: product_manufacturer
#: view:product.product:0
#: field:product.product,manufacturer:0
msgid "Manufacturer"
msgstr "Hersteller"
#~ msgid "Error: Invalid ean code"
#~ msgstr "Fehler: Falscher EAN code"
#~ msgid "Products Attributes & Manufacturers"
#~ msgstr "Produkt-Eigenschaften und -Produzenten"
#~ msgid "A module that add manufacturers and attributes on the product form"
#~ msgstr ""
#~ "Ein Modul, durch das einem Produkt Produzenten und Merkmale hinzgefügt "
#~ "werden können"

View File

@ -1,75 +0,0 @@
# Greek translation for openobject-addons
# Copyright (c) 2010 Rosetta Contributors and Canonical Ltd 2010
# This file is distributed under the same license as the openobject-addons package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2010.
#
msgid ""
msgstr ""
"Project-Id-Version: openobject-addons\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2012-12-21 17:06+0000\n"
"PO-Revision-Date: 2010-11-11 15:58+0000\n"
"Last-Translator: Dimitris Andavoglou <dimitrisand@gmail.com>\n"
"Language-Team: Greek <el@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2014-04-22 07:26+0000\n"
"X-Generator: Launchpad (build 16985)\n"
#. module: product_manufacturer
#: field:product.product,manufacturer_pref:0
msgid "Manufacturer Product Code"
msgstr "Κωδικός Κατασκευαστή"
#. module: product_manufacturer
#: model:ir.model,name:product_manufacturer.model_product_product
#: field:product.manufacturer.attribute,product_id:0
msgid "Product"
msgstr "Προϊόν"
#. module: product_manufacturer
#: view:product.manufacturer.attribute:0
msgid "Product Template Name"
msgstr "Όνομα Προτύπου Προϊόντος"
#. module: product_manufacturer
#: model:ir.model,name:product_manufacturer.model_product_manufacturer_attribute
msgid "Product attributes"
msgstr "Ιδιότητες προϊόντος"
#. module: product_manufacturer
#: view:product.manufacturer.attribute:0
#: view:product.product:0
msgid "Product Attributes"
msgstr "Χαρακτηριστικά Προϊόντος"
#. module: product_manufacturer
#: field:product.manufacturer.attribute,name:0
msgid "Attribute"
msgstr "Χαρακτηριστικό"
#. module: product_manufacturer
#: field:product.manufacturer.attribute,value:0
msgid "Value"
msgstr "Τιμή"
#. module: product_manufacturer
#: view:product.product:0
#: field:product.product,attribute_ids:0
msgid "Attributes"
msgstr "Ιδιότητες"
#. module: product_manufacturer
#: field:product.product,manufacturer_pname:0
msgid "Manufacturer Product Name"
msgstr "Όνομα Προϊόντος Κατασκευαστή"
#. module: product_manufacturer
#: view:product.product:0
#: field:product.product,manufacturer:0
msgid "Manufacturer"
msgstr "Κατασκευαστής"
#~ msgid "Products Attributes & Manufacturers"
#~ msgstr "Ιδιότητες Προϊόντος και Κατασκευαστές"

View File

@ -1,83 +0,0 @@
# Spanish translation for openobject-addons
# Copyright (c) 2010 Rosetta Contributors and Canonical Ltd 2010
# This file is distributed under the same license as the openobject-addons package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2010.
#
msgid ""
msgstr ""
"Project-Id-Version: openobject-addons\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2012-12-21 17:06+0000\n"
"PO-Revision-Date: 2010-12-08 21:40+0000\n"
"Last-Translator: Jordi Esteve (www.zikzakmedia.com) "
"<jesteve@zikzakmedia.com>\n"
"Language-Team: Spanish <es@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2014-04-22 07:26+0000\n"
"X-Generator: Launchpad (build 16985)\n"
#. module: product_manufacturer
#: field:product.product,manufacturer_pref:0
msgid "Manufacturer Product Code"
msgstr "Código producto fabricante"
#. module: product_manufacturer
#: model:ir.model,name:product_manufacturer.model_product_product
#: field:product.manufacturer.attribute,product_id:0
msgid "Product"
msgstr "Producto"
#. module: product_manufacturer
#: view:product.manufacturer.attribute:0
msgid "Product Template Name"
msgstr "Nombre de plantilla de producto"
#. module: product_manufacturer
#: model:ir.model,name:product_manufacturer.model_product_manufacturer_attribute
msgid "Product attributes"
msgstr "Atributos de producto"
#. module: product_manufacturer
#: view:product.manufacturer.attribute:0
#: view:product.product:0
msgid "Product Attributes"
msgstr "Atributos del producto"
#. module: product_manufacturer
#: field:product.manufacturer.attribute,name:0
msgid "Attribute"
msgstr "Atributo"
#. module: product_manufacturer
#: field:product.manufacturer.attribute,value:0
msgid "Value"
msgstr "Valor"
#. module: product_manufacturer
#: view:product.product:0
#: field:product.product,attribute_ids:0
msgid "Attributes"
msgstr "Atributos"
#. module: product_manufacturer
#: field:product.product,manufacturer_pname:0
msgid "Manufacturer Product Name"
msgstr "Nombre producto fabricante"
#. module: product_manufacturer
#: view:product.product:0
#: field:product.product,manufacturer:0
msgid "Manufacturer"
msgstr "Fabricante"
#~ msgid "A module that add manufacturers and attributes on the product form"
#~ msgstr ""
#~ "Un módulo que añade fabricantes y atributos en el formulario de producto"
#~ msgid "Products Attributes & Manufacturers"
#~ msgstr "Fabricantes y atributos de los productos"
#~ msgid "Error: Invalid ean code"
#~ msgstr "Error: Código EAN no válido"

View File

@ -1,84 +0,0 @@
# Spanish translation for openobject-addons
# Copyright (c) 2010 Rosetta Contributors and Canonical Ltd 2010
# This file is distributed under the same license as the openobject-addons package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2010.
#
msgid ""
msgstr ""
"Project-Id-Version: openobject-addons\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2012-12-21 17:06+0000\n"
"PO-Revision-Date: 2012-02-17 00:27+0000\n"
"Last-Translator: Carlos Vásquez (CLEARCORP) "
"<carlos.vasquez@clearcorp.co.cr>\n"
"Language-Team: Spanish <es@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2014-04-22 07:26+0000\n"
"X-Generator: Launchpad (build 16985)\n"
"Language: es\n"
#. module: product_manufacturer
#: field:product.product,manufacturer_pref:0
msgid "Manufacturer Product Code"
msgstr "Código producto fabricante"
#. module: product_manufacturer
#: model:ir.model,name:product_manufacturer.model_product_product
#: field:product.manufacturer.attribute,product_id:0
msgid "Product"
msgstr "Producto"
#. module: product_manufacturer
#: view:product.manufacturer.attribute:0
msgid "Product Template Name"
msgstr "Nombre de plantilla de producto"
#. module: product_manufacturer
#: model:ir.model,name:product_manufacturer.model_product_manufacturer_attribute
msgid "Product attributes"
msgstr "Atributos de producto"
#. module: product_manufacturer
#: view:product.manufacturer.attribute:0
#: view:product.product:0
msgid "Product Attributes"
msgstr "Atributos del producto"
#. module: product_manufacturer
#: field:product.manufacturer.attribute,name:0
msgid "Attribute"
msgstr "Atributo"
#. module: product_manufacturer
#: field:product.manufacturer.attribute,value:0
msgid "Value"
msgstr "Valor"
#. module: product_manufacturer
#: view:product.product:0
#: field:product.product,attribute_ids:0
msgid "Attributes"
msgstr "Atributos"
#. module: product_manufacturer
#: field:product.product,manufacturer_pname:0
msgid "Manufacturer Product Name"
msgstr "Nombre producto fabricante"
#. module: product_manufacturer
#: view:product.product:0
#: field:product.product,manufacturer:0
msgid "Manufacturer"
msgstr "Fabricante"
#~ msgid "Error: Invalid ean code"
#~ msgstr "Error: Código EAN no válido"
#~ msgid "A module that add manufacturers and attributes on the product form"
#~ msgstr ""
#~ "Un módulo que añade fabricantes y atributos en el formulario de producto"
#~ msgid "Products Attributes & Manufacturers"
#~ msgstr "Fabricantes y atributos de los productos"

View File

@ -1,79 +0,0 @@
# Spanish translation for openobject-addons
# Copyright (c) 2010 Rosetta Contributors and Canonical Ltd 2010
# This file is distributed under the same license as the openobject-addons package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2010.
#
msgid ""
msgstr ""
"Project-Id-Version: openobject-addons\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2012-12-21 17:06+0000\n"
"PO-Revision-Date: 2010-09-19 00:08+0000\n"
"Last-Translator: Borja López Soilán (NeoPolus) <borjalopezsoilan@gmail.com>\n"
"Language-Team: Spanish <es@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2014-04-22 07:26+0000\n"
"X-Generator: Launchpad (build 16985)\n"
#. module: product_manufacturer
#: field:product.product,manufacturer_pref:0
msgid "Manufacturer Product Code"
msgstr ""
#. module: product_manufacturer
#: model:ir.model,name:product_manufacturer.model_product_product
#: field:product.manufacturer.attribute,product_id:0
msgid "Product"
msgstr "Producto"
#. module: product_manufacturer
#: view:product.manufacturer.attribute:0
msgid "Product Template Name"
msgstr "Nombre de plantilla de producto"
#. module: product_manufacturer
#: model:ir.model,name:product_manufacturer.model_product_manufacturer_attribute
msgid "Product attributes"
msgstr "Atributos de producto"
#. module: product_manufacturer
#: view:product.manufacturer.attribute:0
#: view:product.product:0
msgid "Product Attributes"
msgstr ""
#. module: product_manufacturer
#: field:product.manufacturer.attribute,name:0
msgid "Attribute"
msgstr "Atributo"
#. module: product_manufacturer
#: field:product.manufacturer.attribute,value:0
msgid "Value"
msgstr "Valor"
#. module: product_manufacturer
#: view:product.product:0
#: field:product.product,attribute_ids:0
msgid "Attributes"
msgstr "Atributos"
#. module: product_manufacturer
#: field:product.product,manufacturer_pname:0
msgid "Manufacturer Product Name"
msgstr ""
#. module: product_manufacturer
#: view:product.product:0
#: field:product.product,manufacturer:0
msgid "Manufacturer"
msgstr "Fabricante"
#~ msgid "A module that add manufacturers and attributes on the product form"
#~ msgstr ""
#~ "Un módulo que añade fabricantes y atributos en el formulario de producto"
#~ msgid "Products Attributes & Manufacturers"
#~ msgstr "Fabricantes y atributos de los productos"

View File

@ -1,89 +0,0 @@
# Spanish translation for openobject-addons
# Copyright (c) 2010 Rosetta Contributors and Canonical Ltd 2010
# This file is distributed under the same license as the openobject-addons package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2010.
#
msgid ""
msgstr ""
"Project-Id-Version: openobject-addons\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2011-01-11 11:15+0000\n"
"PO-Revision-Date: 2010-12-08 21:40+0000\n"
"Last-Translator: Jordi Esteve (www.zikzakmedia.com) "
"<jesteve@zikzakmedia.com>\n"
"Language-Team: Spanish <es@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2011-09-05 05:46+0000\n"
"X-Generator: Launchpad (build 13830)\n"
#. module: product_manufacturer
#: model:ir.module.module,description:product_manufacturer.module_meta_information
msgid "A module that add manufacturers and attributes on the product form"
msgstr ""
"Un módulo que añade fabricantes y atributos en el formulario de producto"
#. module: product_manufacturer
#: field:product.product,manufacturer_pref:0
msgid "Manufacturer Product Code"
msgstr "Código producto fabricante"
#. module: product_manufacturer
#: model:ir.model,name:product_manufacturer.model_product_product
#: field:product.manufacturer.attribute,product_id:0
msgid "Product"
msgstr "Producto"
#. module: product_manufacturer
#: view:product.manufacturer.attribute:0
msgid "Product Template Name"
msgstr "Nombre de plantilla de producto"
#. module: product_manufacturer
#: model:ir.model,name:product_manufacturer.model_product_manufacturer_attribute
msgid "Product attributes"
msgstr "Atributos de producto"
#. module: product_manufacturer
#: view:product.manufacturer.attribute:0
#: view:product.product:0
msgid "Product Attributes"
msgstr "Atributos del producto"
#. module: product_manufacturer
#: field:product.manufacturer.attribute,name:0
msgid "Attribute"
msgstr "Atributo"
#. module: product_manufacturer
#: field:product.manufacturer.attribute,value:0
msgid "Value"
msgstr "Valor"
#. module: product_manufacturer
#: constraint:product.product:0
msgid "Error: Invalid ean code"
msgstr "Error: Código EAN no válido"
#. module: product_manufacturer
#: view:product.product:0
#: field:product.product,attribute_ids:0
msgid "Attributes"
msgstr "Atributos"
#. module: product_manufacturer
#: model:ir.module.module,shortdesc:product_manufacturer.module_meta_information
msgid "Products Attributes & Manufacturers"
msgstr "Fabricantes y atributos de los productos"
#. module: product_manufacturer
#: field:product.product,manufacturer_pname:0
msgid "Manufacturer Product Name"
msgstr "Nombre producto fabricante"
#. module: product_manufacturer
#: view:product.product:0
#: field:product.product,manufacturer:0
msgid "Manufacturer"
msgstr "Fabricante"

View File

@ -1,89 +0,0 @@
# Spanish translation for openobject-addons
# Copyright (c) 2010 Rosetta Contributors and Canonical Ltd 2010
# This file is distributed under the same license as the openobject-addons package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2010.
#
msgid ""
msgstr ""
"Project-Id-Version: openobject-addons\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2011-01-11 11:15+0000\n"
"PO-Revision-Date: 2010-12-08 21:40+0000\n"
"Last-Translator: Jordi Esteve (www.zikzakmedia.com) "
"<jesteve@zikzakmedia.com>\n"
"Language-Team: Spanish <es@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2011-09-05 05:46+0000\n"
"X-Generator: Launchpad (build 13830)\n"
#. module: product_manufacturer
#: model:ir.module.module,description:product_manufacturer.module_meta_information
msgid "A module that add manufacturers and attributes on the product form"
msgstr ""
"Un módulo que añade fabricantes y atributos en el formulario de producto"
#. module: product_manufacturer
#: field:product.product,manufacturer_pref:0
msgid "Manufacturer Product Code"
msgstr "Código producto fabricante"
#. module: product_manufacturer
#: model:ir.model,name:product_manufacturer.model_product_product
#: field:product.manufacturer.attribute,product_id:0
msgid "Product"
msgstr "Producto"
#. module: product_manufacturer
#: view:product.manufacturer.attribute:0
msgid "Product Template Name"
msgstr "Nombre de plantilla de producto"
#. module: product_manufacturer
#: model:ir.model,name:product_manufacturer.model_product_manufacturer_attribute
msgid "Product attributes"
msgstr "Atributos de producto"
#. module: product_manufacturer
#: view:product.manufacturer.attribute:0
#: view:product.product:0
msgid "Product Attributes"
msgstr "Atributos del producto"
#. module: product_manufacturer
#: field:product.manufacturer.attribute,name:0
msgid "Attribute"
msgstr "Atributo"
#. module: product_manufacturer
#: field:product.manufacturer.attribute,value:0
msgid "Value"
msgstr "Valor"
#. module: product_manufacturer
#: constraint:product.product:0
msgid "Error: Invalid ean code"
msgstr "Error: Código EAN no válido"
#. module: product_manufacturer
#: view:product.product:0
#: field:product.product,attribute_ids:0
msgid "Attributes"
msgstr "Atributos"
#. module: product_manufacturer
#: model:ir.module.module,shortdesc:product_manufacturer.module_meta_information
msgid "Products Attributes & Manufacturers"
msgstr "Fabricantes y atributos de los productos"
#. module: product_manufacturer
#: field:product.product,manufacturer_pname:0
msgid "Manufacturer Product Name"
msgstr "Nombre producto fabricante"
#. module: product_manufacturer
#: view:product.product:0
#: field:product.product,manufacturer:0
msgid "Manufacturer"
msgstr "Fabricante"

View File

@ -1,72 +0,0 @@
# Estonian translation for openobject-addons
# Copyright (c) 2010 Rosetta Contributors and Canonical Ltd 2010
# This file is distributed under the same license as the openobject-addons package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2010.
#
msgid ""
msgstr ""
"Project-Id-Version: openobject-addons\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2012-12-21 17:06+0000\n"
"PO-Revision-Date: 2010-06-25 18:10+0000\n"
"Last-Translator: lyyser <Unknown>\n"
"Language-Team: Estonian <et@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2014-04-22 07:26+0000\n"
"X-Generator: Launchpad (build 16985)\n"
#. module: product_manufacturer
#: field:product.product,manufacturer_pref:0
msgid "Manufacturer Product Code"
msgstr "Tootja tootekood"
#. module: product_manufacturer
#: model:ir.model,name:product_manufacturer.model_product_product
#: field:product.manufacturer.attribute,product_id:0
msgid "Product"
msgstr "Toode"
#. module: product_manufacturer
#: view:product.manufacturer.attribute:0
msgid "Product Template Name"
msgstr "Toote malli nimi"
#. module: product_manufacturer
#: model:ir.model,name:product_manufacturer.model_product_manufacturer_attribute
msgid "Product attributes"
msgstr "Toote omadused"
#. module: product_manufacturer
#: view:product.manufacturer.attribute:0
#: view:product.product:0
msgid "Product Attributes"
msgstr "Toote omadused"
#. module: product_manufacturer
#: field:product.manufacturer.attribute,name:0
msgid "Attribute"
msgstr "Atribuut"
#. module: product_manufacturer
#: field:product.manufacturer.attribute,value:0
msgid "Value"
msgstr "Väärtus"
#. module: product_manufacturer
#: view:product.product:0
#: field:product.product,attribute_ids:0
msgid "Attributes"
msgstr "Omadused"
#. module: product_manufacturer
#: field:product.product,manufacturer_pname:0
msgid "Manufacturer Product Name"
msgstr "Tootja tootenimi"
#. module: product_manufacturer
#: view:product.product:0
#: field:product.product,manufacturer:0
msgid "Manufacturer"
msgstr "Tootja"

View File

@ -1,81 +0,0 @@
# Finnish translation for openobject-addons
# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011
# This file is distributed under the same license as the openobject-addons package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2011.
#
msgid ""
msgstr ""
"Project-Id-Version: openobject-addons\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2012-12-21 17:06+0000\n"
"PO-Revision-Date: 2011-06-20 10:09+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Finnish <fi@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2014-04-22 07:26+0000\n"
"X-Generator: Launchpad (build 16985)\n"
#. module: product_manufacturer
#: field:product.product,manufacturer_pref:0
msgid "Manufacturer Product Code"
msgstr "Valmistajan tuotekoodi"
#. module: product_manufacturer
#: model:ir.model,name:product_manufacturer.model_product_product
#: field:product.manufacturer.attribute,product_id:0
msgid "Product"
msgstr "Tuote"
#. module: product_manufacturer
#: view:product.manufacturer.attribute:0
msgid "Product Template Name"
msgstr "Tuotemallipohjan nimi"
#. module: product_manufacturer
#: model:ir.model,name:product_manufacturer.model_product_manufacturer_attribute
msgid "Product attributes"
msgstr "Tuotteen attribuutit"
#. module: product_manufacturer
#: view:product.manufacturer.attribute:0
#: view:product.product:0
msgid "Product Attributes"
msgstr "Tuotteen attribuutit"
#. module: product_manufacturer
#: field:product.manufacturer.attribute,name:0
msgid "Attribute"
msgstr "Attribuutti"
#. module: product_manufacturer
#: field:product.manufacturer.attribute,value:0
msgid "Value"
msgstr "Arvo"
#. module: product_manufacturer
#: view:product.product:0
#: field:product.product,attribute_ids:0
msgid "Attributes"
msgstr "Attribuutit"
#. module: product_manufacturer
#: field:product.product,manufacturer_pname:0
msgid "Manufacturer Product Name"
msgstr "Valmistajan tuotenimi"
#. module: product_manufacturer
#: view:product.product:0
#: field:product.product,manufacturer:0
msgid "Manufacturer"
msgstr "Valmistaja"
#~ msgid "A module that add manufacturers and attributes on the product form"
#~ msgstr "Moduuli jo lisää valmistajia ja attribuutteja tuotelomakkeelle"
#~ msgid "Error: Invalid ean code"
#~ msgstr "Virhe: Väärä EAN-koodi"
#~ msgid "Products Attributes & Manufacturers"
#~ msgstr "Tuotteen attribuutit ja valmistajat"

Some files were not shown because too many files have changed in this diff Show More