diff options
| author | pinky <> | 2006-12-07 13:41:40 +0000 |
|---|---|---|
| committer | pinky <> | 2006-12-07 13:41:40 +0000 |
| commit | a83dce15e0f33145b2eea7e878ae7eacf3aba90a (patch) | |
| tree | a3d1399cbfebf7d90a8894d97c9b7e0a9d881960 /addons/purchase | |
| parent | 004a0b996ff8f269451e07346f71a129a1f3fbaf (diff) | |
New trunk
bzr revid: pinky-dedd7f8a42bd4557112a0513082691b8590ad6cc
Diffstat (limited to 'addons/purchase')
| -rw-r--r-- | addons/purchase/__init__.py | 33 | ||||
| -rw-r--r-- | addons/purchase/__terp__.py | 13 | ||||
| -rw-r--r-- | addons/purchase/partner.py | 49 | ||||
| -rw-r--r-- | addons/purchase/purchase.py | 327 | ||||
| -rw-r--r-- | addons/purchase/purchase_data.xml | 41 | ||||
| -rw-r--r-- | addons/purchase/purchase_demo.xml | 6 | ||||
| -rw-r--r-- | addons/purchase/purchase_report.xml | 17 | ||||
| -rw-r--r-- | addons/purchase/purchase_sequence.xml | 19 | ||||
| -rw-r--r-- | addons/purchase/purchase_view.xml | 165 | ||||
| -rw-r--r-- | addons/purchase/purchase_wizard.xml | 6 | ||||
| -rw-r--r-- | addons/purchase/purchase_workflow.xml | 173 | ||||
| -rw-r--r-- | addons/purchase/report/__init__.py | 29 | ||||
| -rw-r--r-- | addons/purchase/report/order.py | 83 | ||||
| -rw-r--r-- | addons/purchase/report/order.rml | 280 | ||||
| -rw-r--r-- | addons/purchase/report/request_quotation.py | 41 | ||||
| -rw-r--r-- | addons/purchase/report/request_quotation.rml | 227 | ||||
| -rw-r--r-- | addons/purchase/wizard/__init__.py | 28 | ||||
| -rw-r--r-- | addons/purchase/wizard/wizard_group.py | 156 |
18 files changed, 1693 insertions, 0 deletions
diff --git a/addons/purchase/__init__.py b/addons/purchase/__init__.py new file mode 100644 index 00000000000..cb9109254eb --- /dev/null +++ b/addons/purchase/__init__.py @@ -0,0 +1,33 @@ +############################################################################## +# +# Copyright (c) 2004 TINY SPRL. (http://tiny.be) All Rights Reserved. +# Fabien Pinckaers <fp@tiny.Be> +# +# WARNING: This program as such is intended to be used by professional +# programmers who take the whole responsability of assessing all potential +# consequences resulting from its eventual inadequacies and bugs +# End users who are looking for a ready-to-use solution with commercial +# garantees and support are strongly adviced to contract a Free Software +# Service Company +# +# This program is Free Software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +############################################################################## + +import tools +import purchase +import partner +import wizard +import report diff --git a/addons/purchase/__terp__.py b/addons/purchase/__terp__.py new file mode 100644 index 00000000000..031d9291226 --- /dev/null +++ b/addons/purchase/__terp__.py @@ -0,0 +1,13 @@ +{ + "name" : "Purchase Management", + "version" : "1.0", + "author" : "Tiny", + "website" : "http://tinyerp.com/module_purchase.html", + "depends" : ["base", "account", "stock"], + "category" : "Generic Modules/Sales & Purchases", + "init_xml" : [], + "demo_xml" : ["purchase_demo.xml"], + "update_xml" : ["purchase_workflow.xml", "purchase_sequence.xml", "purchase_data.xml", "purchase_view.xml", "purchase_report.xml", "purchase_wizard.xml"], + "active": False, + "installable": True +} diff --git a/addons/purchase/partner.py b/addons/purchase/partner.py new file mode 100644 index 00000000000..227f7fb8a0e --- /dev/null +++ b/addons/purchase/partner.py @@ -0,0 +1,49 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# Copyright (c) 2004-2006 TINY SPRL. (http://tiny.be) All Rights Reserved. +# +# $Id: account.py 1005 2005-07-25 08:41:42Z nicoe $ +# +# WARNING: This program as such is intended to be used by professional +# programmers who take the whole responsability of assessing all potential +# consequences resulting from its eventual inadequacies and bugs +# End users who are looking for a ready-to-use solution with commercial +# garantees and support are strongly adviced to contract a Free Software +# Service Company +# +# This program is Free Software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +############################################################################## + +from osv import fields, osv + +class res_partner(osv.osv): + _name = 'res.partner' + _inherit = 'res.partner' + _columns = { + 'property_product_pricelist_purchase': fields.property( + 'product.pricelist', + type='many2one', + relation='product.pricelist', + string="Purchase Pricelist", + method=True, + view_load=True, + group_name="Pricelists Properties", + help="This pricelist will be used, instead of the default one, for purchases from the current partner"), + } +res_partner() + + diff --git a/addons/purchase/purchase.py b/addons/purchase/purchase.py new file mode 100644 index 00000000000..f62acd6473a --- /dev/null +++ b/addons/purchase/purchase.py @@ -0,0 +1,327 @@ +############################################################################## +# +# Copyright (c) 2004-2006 TINY SPRL. (http://tiny.be) All Rights Reserved. +# +# WARNING: This program as such is intended to be used by professional +# programmers who take the whole responsability of assessing all potential +# consequences resulting from its eventual inadequacies and bugs +# End users who are looking for a ready-to-use solution with commercial +# garantees and support are strongly adviced to contract a Free Software +# Service Company +# +# This program is Free Software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +############################################################################## + +from osv import fields +from osv import osv +import time +import netsvc + +import ir +from mx import DateTime +import pooler + +# +# Model definition +# +class purchase_order(osv.osv): + def _calc_amount(self, cr, uid, ids, prop, unknow_none, unknow_dict): + res = {} + for order in self.browse(cr, uid, ids): + res[order.id] = 0 + for oline in order.order_line: + res[order.id] += oline.price_unit * oline.product_qty + return res + + def _amount_untaxed(self, cr, uid, ids, field_name, arg, context): + id_set = ",".join(map(str, ids)) + cr.execute("SELECT s.id,COALESCE(SUM(l.price_unit*l.product_qty),0)::decimal(16,2) AS amount FROM purchase_order s LEFT OUTER JOIN purchase_order_line l ON (s.id=l.order_id) WHERE s.id IN ("+id_set+") GROUP BY s.id ") + res = dict(cr.fetchall()) + return res + + def _amount_tax(self, cr, uid, ids, field_name, arg, context): + res = {} + for order in self.browse(cr, uid, ids): + val = 0.0 + for line in order.order_line: + for tax in line.taxes_id: + for c in self.pool.get('account.tax').compute(cr, uid, [tax.id], line.price_unit, line.product_qty, order.partner_address_id.id): + val+=c['amount'] + res[order.id]=round(val,2) + return res + + def _amount_total(self, cr, uid, ids, field_name, arg, context): + res = {} + untax = self._amount_untaxed(cr, uid, ids, field_name, arg, context) + tax = self._amount_tax(cr, uid, ids, field_name, arg, context) + for id in ids: + res[id] = untax.get(id, 0.0) + tax.get(id, 0.0) + return res + + _columns = { + 'name': fields.char('Order Description', size=64, required=True, select=True), + 'origin': fields.char('Origin', size=64), + 'ref': fields.char('Order Reference', size=64), + 'partner_ref': fields.char('Partner Reference', size=64), + 'date_order':fields.date('Date Ordered', required=True, states={'confirmed':[('readonly',True)], 'approved':[('readonly',True)]}), + 'date_approve':fields.date('Date Approved'), + 'partner_id':fields.many2one('res.partner', 'Partner', required=True, states={'confirmed':[('readonly',True)], 'approved':[('readonly',True)]}, change_default=True, relate=True), + 'partner_address_id':fields.many2one('res.partner.address', 'Address', required=True, states={'posted':[('readonly',True)]}), + + 'dest_address_id':fields.many2one('res.partner.address', 'Destination Address', states={'posted':[('readonly',True)]}), + 'warehouse_id': fields.many2one('stock.warehouse', 'Warehouse', states={'posted':[('readonly',True)]}, relate=True), + 'location_id': fields.many2one('stock.location', 'Delivery destination', required=True), + 'project_id':fields.many2one('account.analytic.account', 'Analytic Account', states={'posted':[('readonly',True)]}), + + 'pricelist_id':fields.many2one('product.pricelist', 'Pricelist', required=True, states={'confirmed':[('readonly',True)], 'approved':[('readonly',True)]}), + + 'state': fields.selection([('draft', 'Request for Quotation'), ('wait', 'Waiting'), ('confirmed', 'Confirmed'), ('approved', 'Approved'),('except_ship', 'Shipping Exception'), ('except_invoice', 'Invoice Exception'), ('done', 'Done'), ('cancel', 'Cancelled')], 'Order State', readonly=True, help="The state of the purchase order or the quotation request. A quotation is a purchase order in a 'Draft' state. Then the order has to be confirmed by the user, the state switch to 'Confirmed'. Then the supplier must confirm the order to change the state to 'Approved'. When the purchase order is paid and received, the state becomes 'Done'. If a cancel action occurs in the invoice or in the reception of goods, the state becomes in exception.", select=True), + 'order_line': fields.one2many('purchase.order.line', 'order_id', 'Order State', states={'confirmed':[('readonly',True)], 'approved':[('readonly',True)]}), + 'validator' : fields.many2one('res.users', 'Validated by', readonly=True), + 'notes': fields.text('Notes'), + 'invoice_id': fields.many2one('account.invoice', 'Invoice', readonly=True), + 'picking_id': fields.many2one('stock.picking', 'Picking list', readonly=True), + 'shipped':fields.boolean('Received', readonly=True, select=True), + 'invoiced':fields.boolean('Invoiced & Paid', readonly=True, select=True), + 'invoice_method': fields.selection([('manual','Manual'),('order','From order'),('picking','From picking')], 'Invoicing method', required=True), + + 'amount_untaxed': fields.function(_amount_untaxed, method=True, string='Untaxed Amount'), + 'amount_tax': fields.function(_amount_tax, method=True, string='Taxes'), + 'amount_total': fields.function(_amount_total, method=True, string='Total'), + } + _defaults = { + 'date_order': lambda *a: time.strftime('%Y-%m-%d'), + 'state': lambda *a: 'draft', + 'name': lambda obj, cr, uid, context: obj.pool.get('ir.sequence').get(cr, uid, 'purchase.order'), + 'shipped': lambda *a: 0, + 'invoice_method': lambda *a: 'order', + 'invoiced': lambda *a: 0 + } + _name = "purchase.order" + _description = "Purchase order" + + def button_dummy(self, cr, uid, ids, context={}): + return True + + def onchange_dest_address_id(self, cr, uid, ids, adr_id): + if not adr_id: + return {} + part_id = self.pool.get('res.partner.address').read(cr, uid, [adr_id], ['partner_id'])[0]['partner_id'][0] + loc_id = self.pool.get('res.partner').browse(cr, uid, part_id).property_stock_customer[0] + return {'value':{'location_id': loc_id, 'warehouse_id': False}} + + def onchange_warehouse_id(self, cr, uid, ids, warehouse_id): + if not warehouse_id: + return {} + res = self.pool.get('stock.warehouse').read(cr, uid, [warehouse_id], ['lot_input_id'])[0]['lot_input_id'][0] + return {'value':{'location_id': res, 'dest_address_id': False}} + + def onchange_partner_id(self, cr, uid, ids, part): + if not part: + return {'value':{'partner_address_id': False}} + addr = self.pool.get('res.partner').address_get(cr, uid, [part], ['default']) + pricelist = self.pool.get('res.partner').browse(cr, uid, part).property_product_pricelist_purchase[0] + return {'value':{'partner_address_id': addr['default'], 'pricelist_id': pricelist}} + + def wkf_approve_order(self, cr, uid, ids): + self.write(cr, uid, ids, {'state': 'approved', 'date_approve': time.strftime('%Y-%m-%d')}) + return True + + def wkf_confirm_order(self, cr, uid, ids, context={}): + for po in self.browse(cr, uid, ids): + if self.pool.get('res.partner.event.type').check(cr, uid, 'purchase_open'): + self.pool.get('res.partner.event').create(cr, uid, {'name':'Purchase Order: '+po.name, 'partner_id':po.partner_id.id, 'date':time.strftime('%Y-%m-%d %H:%M:%S'), 'user_id':uid, 'partner_type':'retailer', 'probability': 1.0, 'planned_cost':po.amount_untaxed}) + current_name = self.name_get(cr, uid, ids)[0][1] + for id in ids: + self.write(cr, uid, [id], {'state' : 'confirmed', 'validator' : uid}) + return True + + def wkf_warn_buyer(self, cr, uid, ids): + self.write(cr, uid, ids, {'state' : 'wait', 'validator' : uid}) + request = pooler.get_pool(cr.dbname).get('res.request') + for po in self.browse(cr, uid, ids): + managers = [] + for oline in po.order_line: + manager = oline.product_id.product_manager + if manager and not (manager.id in managers): + managers.append(manager.id) + for manager_id in managers: + request.create(cr, uid, + {'name' : "Purchase amount over the limit", + 'act_from' : uid, + 'act_to' : manager_id, + 'body': 'Somebody has just confirmed a purchase with an amount over the defined limit', + 'ref_partner_id': po.partner_id.id, + 'ref_doc1': 'purchase.order,%d' % (po.id,), + }) + + def action_invoice_create(self, cr, uid, ids, *args): + res = False + for o in self.browse(cr, uid, ids): + il = [] + for ol in o.order_line: + + if ol.product_id: + a = ol.product_id.product_tmpl_id.property_account_expense + if not a: + a = ol.product_id.categ_id.property_account_expense_categ[0] + else: + a = a[0] + else: + a = self.pool.get('ir.property').get(cr, uid, 'property_account_expense_categ', 'product.category') + il.append((0, False, { + 'name': ol.name, + 'account_id': a, + 'price_unit': ol.price_unit or 0.0, + 'quantity': ol.product_qty, + 'product_id': ol.product_id.id or False, + 'uos_id': ol.product_uom.id or False, + 'invoice_line_tax_id': [(6, 0, [x.id for x in ol.taxes_id])] + })) + + a = o.partner_id.property_account_payable[0] + inv = { + 'name': o.name, + 'reference': "P%dPO%d" % (o.partner_id.id, o.id), + 'account_id': a, + 'type': 'in_invoice', + 'partner_id': o.partner_id.id, + 'currency_id': o.pricelist_id.currency_id.id, + 'project_id': o.project_id.id, + 'address_invoice_id': o.partner_address_id.id, + 'address_contact_id': o.partner_address_id.id, + 'origin': o.name, + 'invoice_line': il, + } + inv_id = self.pool.get('account.invoice').create(cr, uid, inv) + + self.write(cr, uid, [o.id], {'invoice_id': inv_id}) + res = inv_id + return res + + def has_stockable_product(self,cr, uid, ids, *args): + for order in self.browse(cr, uid, ids): + for order_line in order.order_line: + if order_line.product_id and order_line.product_id.product_tmpl_id.type in ('product', 'consu'): + return True + return False + + def action_picking_create(self,cr, uid, ids, *args): + picking_id = False + for order in self.browse(cr, uid, ids): + loc_id = order.partner_id.property_stock_supplier[0] + istate = 'none' + if order.invoice_method=='picking': + istate = '2binvoiced' + picking_id = self.pool.get('stock.picking').create(cr, uid, { + 'origin': order.name+':'+(order.origin or ''), + 'type': 'in', + 'address_id': order.dest_address_id.id or order.partner_address_id.id, + 'invoice_state': istate + }) + for order_line in order.order_line: + if not order_line.product_id: + continue + if order_line.product_id.product_tmpl_id.type in ('product', 'consu'): + dest = order.location_id.id + self.pool.get('stock.move').create(cr, uid, { + 'name': 'PO:'+order_line.name, + 'product_id': order_line.product_id.id, + 'product_qty': order_line.product_qty, + 'product_uos_qty': order_line.product_qty, + 'product_uom': order_line.product_uom.id, + 'product_uos': order_line.product_uom.id, + 'date_planned': order_line.date_planned, + 'location_id': loc_id, + 'location_dest_id': dest, + 'picking_id': picking_id, + 'move_dest_id': order_line.move_dest_id.id, + 'state': 'assigned' + }) + if order_line.move_dest_id: + self.pool.get('stock.move').write(cr, uid, [order_line.move_dest_id.id], {'location_id':order.location_id.id}) + self.write(cr,uid,[order.id],{'picking_id':picking_id}) + wf_service = netsvc.LocalService("workflow") + wf_service.trg_validate(uid, 'stock.picking', picking_id, 'button_confirm', cr) + return picking_id + def copy(self, cr, uid, id, default=None,context={}): + if not default: + default = {} + default.update({ + 'state':'draft', + 'shipped':False, + 'invoiced':False, + 'invoice_id':False, + 'picking_id':False, + 'name': self.pool.get('ir.sequence').get(cr, uid, 'purchase.order'), + }) + return super(purchase_order, self).copy(cr, uid, id, default, context) + +purchase_order() + +class purchase_order_line(osv.osv): + def _amount_line(self, cr, uid, ids, prop, unknow_none,unknow_dict): + res = {} + for line in self.browse(cr, uid, ids): + res[line.id] = line.price_unit * line.product_qty + return res + + _columns = { + 'name': fields.char('Description', size=64, required=True), + 'product_qty': fields.float('Quantity', required=True, digits=(16,2)), + 'date_planned': fields.date('Date Promised', required=True), + 'taxes_id': fields.many2many('account.tax', 'purchase_order_taxe', 'ord_id', 'tax_id', 'Taxes'), + 'product_uom': fields.many2one('product.uom', 'Product UOM', required=True), + 'product_id': fields.many2one('product.product', 'Product', domain=[('purchase_ok','=',True)], change_default=True, relate=True), + 'move_id': fields.many2one('stock.move', 'Reservation', ondelete='set null'), + 'move_dest_id': fields.many2one('stock.move', 'Reservation Destination', ondelete='set null'), + 'price_unit': fields.float('Unit Price', required=True), + 'price_subtotal': fields.function(_amount_line, method=True, string='Subtotal'), + 'notes': fields.text('Notes'), + 'order_id': fields.many2one('purchase.order', 'Order Ref', select=True) + } + _defaults = { + 'product_qty': lambda *a: 1.0 + } + _table = 'purchase_order_line' + _name = 'purchase.order.line' + _description = 'Purchase Order line' + def copy(self, cr, uid, id, default=None,context={}): + if not default: + default = {} + default.update({'state':'draft', 'move_id':False}) + return super(purchase_order_line, self).copy(cr, uid, id, default, context) + + def product_id_change(self, cr, uid, ids, pricelist, product, qty, uom, partner_id): + if not pricelist: + raise osv.except_osv('No Pricelist !', 'You have to select a pricelist in the sale form !\n Please set one before choosing a product.') + if not product: + return {'value': {'price_unit': 0.0, 'name':'','notes':''}, 'domain':{'product_uom':[]}} + price = self.pool.get('product.pricelist').price_get(cr,uid,[pricelist], product, qty or 1.0, partner_id, {'uom': uom})[pricelist] + prod = self.pool.get('product.product').read(cr, uid, [product], ['taxes_id','name','seller_delay','uom_po_id','description_purchase'])[0] + dt = (DateTime.now() + DateTime.RelativeDateTime(days=prod['seller_delay'] or 0.0)).strftime('%Y-%m-%d') + prod_name = self.pool.get('product.product').name_get(cr, uid, [product])[0][1] + res = {'value': {'price_unit': price, 'name':prod_name, 'taxes_id':prod['taxes_id'], 'date_planned': dt,'notes':prod['description_purchase']}} + domain = {} + if not uom: + res['value']['product_uom'] = prod['uom_po_id'][0] + if res['value']['product_uom']: + res2 = self.pool.get('product.uom').read(cr, uid, [res['value']['product_uom']], ['category_id']) + if res2 and res2[0]['category_id']: + domain = {'product_uom':[('category_id','=',res2[0]['category_id'][0])]} + res['domain'] = domain + return res +purchase_order_line() diff --git a/addons/purchase/purchase_data.xml b/addons/purchase/purchase_data.xml new file mode 100644 index 00000000000..60118ccb543 --- /dev/null +++ b/addons/purchase/purchase_data.xml @@ -0,0 +1,41 @@ +<?xml version="1.0"?> +<terp> +<data noupdate="1"> + <record model="res.request.link" id="req_link_purchase_order"> + <field name="name">Purchase Order</field> + <field name="object">purchase.order</field> + </record> + + <record model="res.partner.event.type" id="event_type_purchase_open"> + <field name="name">Purchase: Purchase Confirmation</field> + <field name="key">purchase_open</field> + </record> + + <record model="product.pricelist.type" id="pricelist_type_purchase"> + <field name="name">Purchase Pricelist</field> + <field name="key">purchase</field> + </record> + + <record model="product.pricelist" id="list0"> + <field name="name">Default Purchase Pricelist</field> + <field name="type">purchase</field> + <field name="currency_id" ref="base.EUR"/> + </record> + <record model="product.pricelist.version" id="ver0"> + <field name="pricelist_id" ref="list0"/> + <field name="name">Default Purchase Pricelist Version</field> + </record> + <record model="product.pricelist.item" id="item0"> + <field name="price_version_id" ref="ver0"/> + <field name="base" ref="product.standard_price"/> + <field name="name">Default Purchase Pricelist Line</field> + </record> + + <record model="ir.property" id="property_product_pricelist_purchase" forcecreate="True"> + <field name="name">property_product_pricelist_purchase</field> + <field name="fields_id" search="[('model','=','res.partner'),('name','=','property_product_pricelist_purchase')]"/> + <field name="value" eval="'product.pricelist,'+str(list0)"/> + </record> + +</data> +</terp> diff --git a/addons/purchase/purchase_demo.xml b/addons/purchase/purchase_demo.xml new file mode 100644 index 00000000000..98793851804 --- /dev/null +++ b/addons/purchase/purchase_demo.xml @@ -0,0 +1,6 @@ +<?xml version="1.0"?> +<terp> +<data noupdate="1"> + +</data> +</terp> diff --git a/addons/purchase/purchase_report.xml b/addons/purchase/purchase_report.xml new file mode 100644 index 00000000000..fae439191bf --- /dev/null +++ b/addons/purchase/purchase_report.xml @@ -0,0 +1,17 @@ +<?xml version="1.0"?> +<terp> + <data> + <report id="report_purchase_quotation" + name="purchase.quotation" + string="Request for Quotation" + model="purchase.order" + rml="purchase/report/request_quotation.rml" + auto="False"/> + <report id="report_purchase_order" + name="purchase.order" + string="Print Order" + model="purchase.order" + rml="purchase/report/order.rml" + auto="False"/> + </data> +</terp> diff --git a/addons/purchase/purchase_sequence.xml b/addons/purchase/purchase_sequence.xml new file mode 100644 index 00000000000..b7474816865 --- /dev/null +++ b/addons/purchase/purchase_sequence.xml @@ -0,0 +1,19 @@ +<?xml version="1.0"?> +<terp> +<data noupdate="1"> + # + # Sequences for purchase.order + # + + <record model="ir.sequence.type" id="seq_type_purchase_order"> + <field name="name">Purchase Order</field> + <field name="code">purchase.order</field> + </record> + <record model="ir.sequence" id="seq_purchase_order"> + <field name="name">Purchase Order</field> + <field name="code">purchase.order</field> + <field name="prefix">PO/</field> + <field name="padding">3</field> + </record> +</data> +</terp> diff --git a/addons/purchase/purchase_view.xml b/addons/purchase/purchase_view.xml new file mode 100644 index 00000000000..5940186796b --- /dev/null +++ b/addons/purchase/purchase_view.xml @@ -0,0 +1,165 @@ +<?xml version="1.0"?> +<terp> +<data> + + <menuitem name="Purchase Management" id="menu_purchase_root" icon="terp-purchase"/> + + <record model="ir.ui.view" id="purchase_order_form"> + <field name="name">purchase.order.form</field> + <field name="model">purchase.order</field> + <field name="type">form</field> + <field name="arch" type="xml"> + <form string="Purchase Order"> + <notebook> + <page string="Purchase Order"> + <field name="name" select="1"/> + <group colspan="2" col="4"> + <field name="shipped" select="1"/> + <field name="invoiced" select="1"/> + </group> + <newline/> + <field name="warehouse_id" on_change="onchange_warehouse_id(warehouse_id)"/> + <field name="date_order" select="1"/> + <field name="partner_id" select="1" on_change="onchange_partner_id(partner_id)"/> + <field name="partner_address_id"/> + <field name="pricelist_id" domain="[('type','=','sale')]"/> + <field name="project_id" select="1"/> + <field name="ref" select="1"/> + <field name="partner_ref" select="1"/> + <newline/> + <field name="order_line" widget="one2many_list" colspan="4" nolabel="1"/> + <separator string="Document State" colspan="4" select="1"/> + <group colspan="4" col="7"> + <field name="amount_untaxed"/> + <field name="amount_tax"/> + <field name="amount_total"/> + <button name="button_dummy" string="Compute" states="draft" type="object"/> + </group> + <group col="9" colspan="4"> + <field name="state" readonly="1"/> + <button name="purchase_confirm" states="draft" string="Confirm Purchase Order"/> + <button name="purchase_appbuyer" states="wait_auth" string="Approve Purchase"/> + <button name="purchase_approve" states="confirmed" string="Approved by Supplier"/> + <button name="purchase_cancel" states="draft,confirmed,wait_auth" string="Cancel Purchase Order"/> + + <button name="purchase_cancel" states="approved" string="Cancel Purchase Order"/> + + <button name="shipping_ok" states="except_shipping" string="Shipping Done"/> + <button name="invoice_ok" states="except_invoice" string="Invoice Done"/> + </group> + </page> + <page string="Purchase Shippings"> + <field name="dest_address_id" on_change="onchange_dest_address_id(dest_address_id)"/> + <field name="location_id"/> + <field name="invoice_method"/> + <newline/> + <field name="picking_id" colspan="3"/> + <field name="invoice_id" colspan="3"/> + </page> + <page string="Notes"> + <field name="validator" /> + <field name="date_approve" /> + <field name="origin" select="1"/> + <field name="notes" colspan="4" nolabel="1"/> + </page> + </notebook> + </form> + </field> + </record> + + <record model="ir.actions.act_window" id="purchase_form_action"> + <field name="name">purchase.order.form</field> + <field name="type">ir.actions.act_window</field> + <field name="res_model">purchase.order</field> + <field name="view_mode">form,tree</field> + </record> + <menuitem name="Purchase Management/Purchase Order" id="menu_purchase_form_action" action="purchase_form_action"/> + <record model="ir.actions.act_window" id="purchase_form_action2"> + <field name="name">purchase.order.form</field> + <field name="type">ir.actions.act_window</field> + <field name="res_model">purchase.order</field> + <field name="domain">[('state','=','draft')]</field> + <field name="view_type">form</field> + <field name="view_mode">tree,form</field> + </record> + <menuitem name="Purchase Management/Purchase Order/Draft Purchase Order (RFQ)" action="purchase_form_action2" id="menu_purchase_order_draft"/> + <record model="ir.actions.act_window" id="purchase_form_action3"> + <field name="name">purchase.order.form</field> + <field name="type">ir.actions.act_window</field> + <field name="res_model">purchase.order</field> + <field name="view_type">form</field> + <field name="view_mode">tree,form</field> + <field name="domain">[('state','=','confirmed')]</field> + </record> + <menuitem name="Purchase Management/Purchase Order/Purchase Order waiting Approval" id="menu_purchase_form_action3" action="purchase_form_action3"/> + <record model="ir.actions.act_window" id="purchase_form_action4"> + <field name="name">purchase.order.form</field> + <field name="type">ir.actions.act_window</field> + <field name="res_model">purchase.order</field> + <field name="view_type">form</field> + <field name="view_mode">tree,form</field> + <field name="domain">[('state','=','approved')]</field> + </record> + <menuitem name="Purchase Management/Purchase Order/Running Purchase Order" id="menu_purchase_form_action4" action="purchase_form_action4"/> + + <record model="ir.ui.view" id="purchase_order_tree"> + <field name="name">purchase.order.tree</field> + <field name="model">purchase.order</field> + <field name="type">tree</field> + <field name="arch" type="xml"> + <tree string="Purchase Order"> + <field name="name"/> + <field name="date_order"/> + <field name="partner_id"/> + <field name="location_id"/> + <field name="origin"/> + <field name="invoiced" string="Paid"/> + <field name="shipped"/> + <field name="amount_untaxed"/> + <field name="state"/> + </tree> + </field> + </record> + + <record model="ir.ui.view" id="purchase_order_line_form"> + <field name="name">purchase.order.line.form</field> + <field name="model">purchase.order.line</field> + <field name="type">form</field> + <field name="arch" type="xml"> + <form string="Purchase Order Line"> + <notebook> + <page string="Order Line"> + <field name="product_qty"/> + <field name="product_uom"/> + <field name="product_id" colspan="3" on_change="product_id_change(parent.pricelist_id,product_id,product_qty,product_uom,parent.partner_id)" context="partner_id=parent.partner_id,quantity=product_qty,pricelist=parent.pricelist_id,uom=product_uom,warehouse=parent.warehouse_id"/> + <field name="name" colspan="3"/> + <field name="date_planned"/> + <field name="price_unit"/> + <field name="price_subtotal" readonly="1" /> + <field name="taxes_id" colspan="3"/> + </page><page string="Notes"> + <field name="notes" colspan="3" nolabel="1"/> + </page> + </notebook> + </form> + </field> + </record> + + <record model="ir.ui.view" id="purchase_order_line_tree"> + <field name="name">purchase.order.line.tree</field> + <field name="model">purchase.order.line</field> + <field name="type">tree</field> + <field name="arch" type="xml"> + <tree string="Purchase Order Line" colors="red:date_planned<=current_date"> + <field name="date_planned"/> + <field name="name"/> + <field name="product_id"/> + <field name="product_qty"/> + <field name="product_uom"/> + <field name="price_unit"/> + <field name="price_subtotal"/> + </tree> + </field> + </record> +</data> +</terp> diff --git a/addons/purchase/purchase_wizard.xml b/addons/purchase/purchase_wizard.xml new file mode 100644 index 00000000000..f5efee29242 --- /dev/null +++ b/addons/purchase/purchase_wizard.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" ?> +<terp> + <data> + <wizard string="Merge purchase" model="purchase.order" name="purchase.order.merge" /> + </data> +</terp> diff --git a/addons/purchase/purchase_workflow.xml b/addons/purchase/purchase_workflow.xml new file mode 100644 index 00000000000..d188fdef05c --- /dev/null +++ b/addons/purchase/purchase_workflow.xml @@ -0,0 +1,173 @@ +<?xml version="1.0"?> +<terp> +<data> + + <record model="workflow" id="purchase_order"> + <field name="name">Purchase Order Basic Workflow</field> + <field name="osv">purchase.order</field> + <field name="on_create">True</field> + </record> + + <record model="workflow.activity" id="act_draft"> + <field name="wkf_id" ref="purchase_order"/> + <field name="flow_start">True</field> + <field name="name">draft</field> + </record> + <record model="workflow.activity" id="act_confirmed"> + <field name="wkf_id" ref="purchase_order"/> + <field name="name">confirmed</field> + <field name="kind">function</field> + <field name="action">wkf_confirm_order()</field> + </record> + <record model="workflow.activity" id="act_cancel"> + <field name="wkf_id" ref="purchase_order"/> + <field name="name">cancel</field> + <field name="action">write({'state':'cancel'})</field> + <field name="kind">stopall</field> + <field name="flow_stop">True</field> + </record> + <record model="workflow.activity" id="act_except_invoice"> + <field name="wkf_id" ref="purchase_order"/> + <field name="name">except_invoice</field> + <field name="kind">function</field> + <field name="action">write({'state':'except_invoice'})</field> + </record> + <record model="workflow.activity" id="act_except_picking"> + <field name="wkf_id" ref="purchase_order"/> + <field name="name">except_picking</field> + <field name="kind">function</field> + <field name="action">write({'state':'except_picking'})</field> + </record> + <record model="workflow.activity" id="act_router"> + <field name="wkf_id" ref="purchase_order"/> + <field name="name">router</field> + <field name="split_mode">OR</field> + <field name="kind">function</field> + <field name="action">wkf_approve_order()</field> + </record> + <record model="workflow.activity" id="act_invoice"> + <field name="wkf_id" ref="purchase_order"/> + <field name="name">invoice</field> + <field name="kind">subflow</field> + <field name="subflow_id" type="int" search="[('osv','=','account.invoice')]"/> + <field name="action">action_invoice_create()</field> + </record> + <record model="workflow.activity" id="act_invoice_done"> + <field name="wkf_id" ref="purchase_order"/> + <field name="name">invoice_done</field> + <field name="action">write({'invoiced':1})</field> + <field name="kind">function</field> + </record> + <record model="workflow.activity" id="act_picking"> + <field name="wkf_id" ref="purchase_order"/> + <field name="name">picking</field> + <field name="kind">subflow</field> + <field name="subflow_id" search="[('osv','=','stock.picking')]"/> + <field name="action">action_picking_create()</field> + </record> + <record model="workflow.activity" id="act_picking_done"> + <field name="wkf_id" ref="purchase_order"/> + <field name="name">picking_done</field> + <field name="action">write({'shipped':1})</field> + <field name="kind">function</field> + <field name="signal_send">subflow.delivery_done</field> + </record> + <record model="workflow.activity" id="act_done"> + <field name="wkf_id" ref="purchase_order"/> + <field name="name">done</field> + <field name="action">write({'state':'done'})</field> + <field name="kind">function</field> + <field name="flow_stop">True</field> + <field name="join_mode">AND</field> + </record> + + <record model="workflow.transition" id="trans_draft_confirmed"> + <field name="act_from" ref="act_draft"/> + <field name="act_to" ref="act_confirmed"/> + <field name="signal">purchase_confirm</field> + </record> + <record model="workflow.transition" id="trans_confirmed_cancel"> + <field name="act_from" ref="act_confirmed"/> + <field name="act_to" ref="act_cancel"/> + <field name="signal">purchase_cancel</field> + </record> + <record model="workflow.transition" id="trans_draft_cancel"> + <field name="act_from" ref="act_draft"/> + <field name="act_to" ref="act_cancel"/> + <field name="signal">purchase_cancel</field> + </record> + <record model="workflow.transition" id="trans_confirmed_router"> + <field name="act_from" ref="act_confirmed"/> + <field name="act_to" ref="act_router"/> + <field name="signal">purchase_approve</field> + </record> + <record model="workflow.transition" id="trans_router_picking"> + <field name="act_from" ref="act_router"/> + <field name="act_to" ref="act_picking"/> + <field name="condition">has_stockable_product()</field> + </record> + <record model="workflow.transition" id="trans_router_picking_done"> + <field name="act_from" ref="act_router"/> + <field name="act_to" ref="act_picking_done"/> + <field name="condition">not has_stockable_product()</field> + </record> + <record model="workflow.transition" id="trans_router_invoice"> + <field name="act_from" ref="act_router"/> + <field name="act_to" ref="act_invoice"/> + <field name="condition">invoice_method=='order'</field> + </record> + <record model="workflow.transition" id="trans_router_invoice_no_order"> + <field name="act_from" ref="act_router"/> + <field name="act_to" ref="act_invoice_done"/> + <field name="condition">invoice_method<>'order'</field> + </record> + <record model="workflow.transition" id="trans_except_picking_picking_done"> + <field name="act_from" ref="act_except_picking"/> + <field name="act_to" ref="act_picking_done"/> + <field name="signal">picking_ok</field> + </record> + <record model="workflow.transition" id="trans_except_invoice_invoice_done"> + <field name="act_from" ref="act_except_invoice"/> + <field name="act_to" ref="act_invoice_done"/> + <field name="signal">invoice_ok</field> + </record> + <record model="workflow.transition" id="trans_except_picking"> + <field name="act_from" ref="act_except_picking"/> + <field name="act_to" ref="act_cancel"/> + <field name="signal">cancel</field> + </record> + <record model="workflow.transition" id="trans_except_invoice"> + <field name="act_from" ref="act_except_invoice"/> + <field name="act_to" ref="act_cancel"/> + <field name="signal">cancel</field> + </record> + <record model="workflow.transition" id="trans_picking_except_picking"> + <field name="act_from" ref="act_picking"/> + <field name="act_to" ref="act_except_picking"/> + <field name="signal">subflow.cancel</field> + </record> + <record model="workflow.transition" id="trans_invoice_except_invoice"> + <field name="act_from" ref="act_invoice"/> + <field name="act_to" ref="act_except_invoice"/> + <field name="signal">subflow.cancel</field> + </record> + <record model="workflow.transition" id="trans_picking_picking_done"> + <field name="act_from" ref="act_picking"/> + <field name="act_to" ref="act_picking_done"/> + <field name="signal">subflow.done</field> + </record> + <record model="workflow.transition" id="trans_invoice_invoice_done"> + <field name="act_from" ref="act_invoice"/> + <field name="act_to" ref="act_invoice_done"/> + <field name="signal">subflow.paid</field> + </record> + <record model="workflow.transition" id="trans_picking_done_done"> + <field name="act_from" ref="act_picking_done"/> + <field name="act_to" ref="act_done"/> + </record> + <record model="workflow.transition" id="trans_invoice_done_done"> + <field name="act_from" ref="act_invoice_done"/> + <field name="act_to" ref="act_done"/> + </record> + </data> +</terp> diff --git a/addons/purchase/report/__init__.py b/addons/purchase/report/__init__.py new file mode 100644 index 00000000000..8573f4ae20b --- /dev/null +++ b/addons/purchase/report/__init__.py @@ -0,0 +1,29 @@ +############################################################################## +# +# Copyright (c) 2004-2006 TINY SPRL. (http://tiny.be) All Rights Reserved. +# +# WARNING: This program as such is intended to be used by professional +# programmers who take the whole responsability of assessing all potential +# consequences resulting from its eventual inadequacies and bugs +# End users who are looking for a ready-to-use solution with commercial +# garantees and support are strongly adviced to contract a Free Software +# Service Company +# +# This program is Free Software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +############################################################################## + +import order +import request_quotation diff --git a/addons/purchase/report/order.py b/addons/purchase/report/order.py new file mode 100644 index 00000000000..2ffddae38c0 --- /dev/null +++ b/addons/purchase/report/order.py @@ -0,0 +1,83 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Copyright (c) 2006 TINY SPRL. (http://tiny.be) All Rights Reserved. +# +# WARNING: This program as such is intended to be used by professional +# programmers who take the whole responsability of assessing all potential +# consequences resulting from its eventual inadequacies and bugs +# End users who are looking for a ready-to-use solution with commercial +# garantees and support are strongly adviced to contract a Free Software +# Service Company +# +# This program is Free Software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +############################################################################## + +import time +from report import report_sxw +from osv import osv +import pooler + +class order(report_sxw.rml_parse): + def __init__(self, cr, uid, name, context): + super(order, self).__init__(cr, uid, name, context) + self.localcontext.update({ + 'time': time, + 'get_line_tax': self._get_line_tax, + 'get_tax': self._get_tax, + }) + def _get_line_tax(self, line_obj): + self.cr.execute("SELECT tax_id FROM purchase_order_taxe WHERE order_line_id=%d" % (line_obj.id)) + res = self.cr.fetchall() or None + if not res: + return "" + if isinstance(res, list): + tax_ids = [t[0] for t in res] + else: + tax_ids = res[0] + res = [tax.name for tax in pooler.get_pool(cr.dbname).get('account.tax').browse(self.cr, self.uid, tax_ids)] + return ",\n ".join(res) + + def _get_tax(self, order_obj): + self.cr.execute("SELECT DISTINCT tax_id FROM purchase_order_taxe, purchase_order_line, purchase_order \ + WHERE (purchase_order_line.order_id=purchase_order.id) AND (purchase_order.id=%d)" % (order_obj.id)) + res = self.cr.fetchall() or None + if not res: + return [] + if isinstance(res, list): + tax_ids = [t[0] for t in res] + else: + tax_ids = res[0] + tax_obj = pooler.get_pool(cr.dbname).get('account.tax') + res = [] + for tax in tax_obj.browse(self.cr, self.uid, tax_ids): + self.cr.execute("SELECT DISTINCT order_line_id FROM purchase_order_line, purchase_order_taxe \ + WHERE (purchase_order_taxe.tax_id=%d) AND (purchase_order_line.order_id=%d)" % (tax.id, order_obj.id)) + lines = self.cr.fetchall() or None + if lines: + if isinstance(lines, list): + line_ids = [l[0] for l in lines] + else: + line_ids = lines[0] + base = 0 + for line in pooler.get_pool(cr.dbname).get('purchase.order.line').browse(self.cr, self.uid, line_ids): + base += line.price_subtotal + res.append({'code':tax.name, + 'base':base, + 'amount':base*tax.amount}) + return res + +report_sxw.report_sxw('report.purchase.order','purchase.order','addons/purchase/report/order.rml',parser=order) diff --git a/addons/purchase/report/order.rml b/addons/purchase/report/order.rml new file mode 100644 index 00000000000..5d82d220d72 --- /dev/null +++ b/addons/purchase/report/order.rml @@ -0,0 +1,280 @@ +<?xml version="1.0"?> +<document filename="test.pdf"> + <template pageSize="(595.0,842.0)" title="Test" author="Martin Simon" allowSplitting="20"> + <pageTemplate id="first"> + <frame id="first" x1="42.0" y1="42.0" width="511" height="758"/> + </pageTemplate> + </template> + <stylesheet> + <blockTableStyle id="Standard_Outline"> + <blockAlignment value="LEFT"/> + <blockValign value="TOP"/> + </blockTableStyle> + <blockTableStyle id="Tableau6"> + <blockAlignment value="LEFT"/> + <blockValign value="TOP"/> + </blockTableStyle> + <blockTableStyle id="Tableau10"> + <blockAlignment value="LEFT"/> + <blockValign value="TOP"/> + </blockTableStyle> + <blockTableStyle id="Tableau2"> + <blockAlignment value="LEFT"/> + <blockValign value="TOP"/> + <lineStyle kind="GRID" colorName="black"/> + <blockBackground colorName="#e6e6e6" start="0,0" stop="0,0"/> + <blockBackground colorName="#e6e6e6" start="1,0" stop="1,0"/> + <blockBackground colorName="#e6e6e6" start="2,0" stop="2,0"/> + <blockBackground colorName="#e6e6e6" start="3,0" stop="3,0"/> + <blockBackground colorName="#e6e6e6" start="4,0" stop="4,0"/> + <blockBackground colorName="#e6e6e6" start="5,0" stop="5,0"/> + <blockBackground colorName="#e6e6e6" start="6,0" stop="6,0"/> + </blockTableStyle> + <blockTableStyle id="Tableau3"> + <blockAlignment value="LEFT"/> + <blockValign value="TOP"/> + </blockTableStyle> + <blockTableStyle id="Tableau5"> + <blockAlignment value="LEFT"/> + <blockValign value="TOP"/> + </blockTableStyle> + <blockTableStyle id="Tableau1"> + <blockAlignment value="LEFT"/> + <blockValign value="TOP"/> + </blockTableStyle> + <blockTableStyle id="Tableau4"> + <blockAlignment value="LEFT"/> + <blockValign value="TOP"/> + </blockTableStyle> + <initialize> + <paraStyle name="all" alignment="justify"/> + </initialize> + <paraStyle name="P1" fontName="Times-Roman" fontSize="9.0" leading="11"/> + <paraStyle name="P2" fontName="Times-Bold" fontSize="10.0" leading="13"/> + <paraStyle name="P3" fontName="Times-Roman" fontSize="10.0" leading="13"/> + <paraStyle name="P4" fontName="Times-Roman" fontSize="10.0" leading="13"/> + <paraStyle name="P5" fontName="Times-Roman" fontSize="10.0" leading="13"/> + <paraStyle name="P6" fontName="Times-Roman" fontSize="9.0" leading="11"/> + <paraStyle name="P7" fontName="Times-Roman" fontSize="11.0" leading="14"/> + <paraStyle name="P8" fontName="Times-Roman" fontSize="4.0" leading="5"/> + <paraStyle name="P9" fontName="Times-Roman"/> + <paraStyle name="P10" fontName="Times-Roman" fontSize="16.0" leading="20"/> + <paraStyle name="P11" fontName="Times-Roman" fontSize="11.0" leading="14"/> + <paraStyle name="P12" fontName="Times-Bold" fontSize="8.0" leading="10"/> + <paraStyle name="P13" fontName="Times-Bold" fontSize="8.0" leading="10" alignment="CENTER"/> + <paraStyle name="P14" fontName="Times-BoldItalic" fontSize="6.0" leading="8" alignment="LEFT"/> + <paraStyle name="P15" fontName="Times-Roman" fontSize="9.0" leading="11" alignment="LEFT"/> + <paraStyle name="P16" fontName="Times-Roman" fontSize="9.0" leading="11" alignment="CENTER"/> + <paraStyle name="P17" fontName="Times-Roman" fontSize="9.0" leading="11" alignment="RIGHT"/> + <paraStyle name="P18" fontName="Times-BoldItalic" fontSize="9.0" leading="11" alignment="LEFT"/> + <paraStyle name="P19" fontName="Times-Italic" fontSize="9.0" leading="11" alignment="LEFT"/> + <paraStyle name="P20" fontName="Times-Roman" fontSize="7.0" leading="9"/> + <paraStyle name="P21" fontName="Times-Bold" fontSize="9.0" leading="11" alignment="RIGHT"/> + <paraStyle name="P22" fontName="Times-Roman" fontSize="7.0" leading="9"/> + <paraStyle name="P23" fontName="Times-Bold" fontSize="9.0" leading="11" alignment="RIGHT"/> + <paraStyle name="P24" fontName="Times-Bold" fontSize="9.0" leading="11"/> + <paraStyle name="P25" fontName="Times-Roman" alignment="LEFT"/> + <paraStyle name="Standard" fontName="Times-Roman"/> + <paraStyle name="Text body" fontName="Times-Roman" spaceBefore="0.0" spaceAfter="6.0"/> + <paraStyle name="Heading" fontName="Helvetica" fontSize="14.0" leading="17" spaceBefore="12.0" spaceAfter="6.0"/> + <paraStyle name="List" fontName="Times-Roman" spaceBefore="0.0" spaceAfter="6.0"/> + <paraStyle name="Table Contents" fontName="Times-Roman"/> + <paraStyle name="Table Heading" fontName="Times-Roman" alignment="CENTER"/> + <paraStyle name="Caption" fontName="Times-Roman" fontSize="12.0" leading="15" spaceBefore="6.0" spaceAfter="6.0"/> + <paraStyle name="Index" fontName="Times-Roman"/> + </stylesheet> + <story> + <para style="P1">[[ repeatIn(objects,'o') ]]</para> + <para style="P1"> + <font color="white"> </font> + </para> + <blockTable colWidths="241.0,57.0,213.0" style="Tableau6"> + <tr> + <td> + <blockTable colWidths="241.0" style="Tableau10"> + <tr> + <td> + <para style="P2">Shipping address :</para> + </td> + </tr> + <tr> + <td> + <para style="P3">[[ repeatIn(o.dest_address_id and [o.dest_address_id] or [],'addr') ]]</para> + <para style="P4">[[ o.partner_id.title or '' ]] [[ o.partner_id.name ]]</para> + <para style="P4">[[ addr.title or '' ]] [[ addr.name ]]</para> + <para style="P4">[[ addr.street ]]</para> + <para style="P4">[[ addr.street2 or '' ]]</para> + <para style="P5"><font face="Times-Roman">[[ addr.zip or '' ]]</font> [[ addr.city or '' ]]</para> + <para style="P5">[[ addr.state_id and addr.state_id.name or '' ]]</para> + <para style="P5">[[ addr.country_id and addr.country_id.name or '' ]]</para> + </td> + </tr> + </blockTable> + <para style="P6"> + <font color="white"> </font> + </para> + </td> + <td> + <para style="P6"> + <font color="white"> </font> + </para> + </td> + <td> + <para style="P7">[[ o.partner_id.title or '' ]] [[ o.partner_id.name ]]</para> + <para style="P7">[[ o.partner_address_id.title or '' ]] [[ o.partner_address_id.name ]]</para> + <para style="P7">[[ o.partner_address_id.street ]]</para> + <para style="P7">[[ o.partner_address_id.street2 or '' ]]</para> + <para style="P7">[[ o.partner_address_id.zip or '' ]] [[ o.partner_address_id.city or '' ]]</para> + <para style="P7">[[ o.partner_address_id.state_id and o.partner_address_id.state_id.name or '' ]]</para> + <para style="P7">[[ o.partner_address_id.country_id and o.partner_address_id.country_id.name or '' ]]</para> + <para style="P8"> + <font color="white"> </font> + </para> + <para style="P4">Tél. : [[ o.partner_address_id.phone or '' ]]</para> + <para style="P4">Fax : [[ o.partner_address_id.fax or '' ]]</para> + <para style="P4">TVA : [[ o.partner_id.vat or '' ]]</para> + </td> + </tr> + </blockTable> + <para style="P9"> + <font color="white"> </font> + </para> + <para style="P10">[[ o.state=='draft' and 'Quotation ' or 'Order ' ]] N° : [[ o.name ]]</para> + <para style="P10"> + <font color="white"> </font> + </para> + <para style="P11">Our order reference : [[ o.ref or '' ]]</para> + <para style="P11">Your order reference : [[ o.partner_ref or '' ]]</para> + <para style="P11">Date ordered : [[ time.strftime('%d/%m/%Y', time.strptime(o.date_order, '%Y-%m-%d')) ]]</para> + <para style="P11">Validated by : [[ o.validator and o.validator.name or '' ]]</para> + <para style="P9"> + <font color="white"> </font> + </para> + <blockTable colWidths="195.0,65.0,46.0,42.0,28.0,65.0,69.0" repeatRows="1" style="Tableau2"> + <tr> + <td> + <para style="P12">Product Description</para> + </td> + <td> + <para style="P13">Applicable Taxes</para> + </td> + <td> + <para style="P13">Date Promised</para> + </td> + <td> + <para style="P13">Qty</para> + </td> + <td> + <para style="P13">UoM</para> + </td> + <td> + <para style="P13">Unit Price</para> + </td> + <td> + <para style="P13">Net Price</para> + </td> + </tr> + </blockTable> + <section> + <para style="P14">[[ repeatIn(o.order_line,'line') ]]</para> + <blockTable colWidths="195.0,65.0,45.0,42.0,28.0,65.0,69.0" style="Tableau3"> + <tr> + <td> + <para style="P15">[[ line.name ]]</para> + </td> + <td> + <para style="P16">[[ ', '.join(map(lambda x: x.name, line.taxes_id))]]</para> + </td> + <td> + <para style="P16">[[ time.strftime('%d/%m/%Y', time.strptime( line.date_planned, '%Y-%m-%d')) ]]</para> + </td> + <td> + <para style="P17">[[ line.product_qty ]]</para> + </td> + <td> + <para style="P15">[[ line.product_uom.name ]]</para> + </td> + <td> + <para style="P17">[[ '%.2f' % line.price_unit ]]</para> + </td> + <td> + <para style="P17">[[ '%.2f' % line.price_subtotal ]]</para> + </td> + </tr> + </blockTable> + <blockTable colWidths="43.0,333.0,135.0" style="Tableau5"> + <tr> + <td> + <para style="P18">[[ repeatIn((line.notes and [line.notes]) or [], 'l') ]]</para> + </td> + <td> + <para style="P19">[[ l or removeParentNode('table') ]]</para> + </td> + <td> + <para style="P18"> + <font color="white"> </font> + </para> + </td> + </tr> + </blockTable> + </section> + <para style="Standard"> + <font color="white"> </font> + </para> + <blockTable colWidths="376.0,65.0,69.0" style="Tableau1"> + <tr> + <td> + <para style="P20"> + <font color="white"> </font> + </para> + </td> + <td> + <para style="P21">Net Total :</para> + </td> + <td> + <para style="P21">[[ '%.2f' % o.amount_untaxed ]] [[ o.pricelist_id.currency_id.name ]]</para> + </td> + </tr> + <tr> + <td> + <para style="P20"> + <font color="white"> </font> + </para> + </td> + <td> + <para style="P21">Taxes :</para> + </td> + <td> + <para style="P21">[[ '%.2f' % o.amount_tax ]] [[ o.pricelist_id.currency_id.name ]]</para> + </td> + </tr> + <tr> + <td> + <para style="P22"> + <font color="white"> </font> + </para> + </td> + <td> + <para style="P23">TOTAL :</para> + </td> + <td> + <para style="P23">[[ '%.2f' % o.amount_total ]] [[ o.pricelist_id.currency_id.name ]]</para> + </td> + </tr> + </blockTable> + <para style="P24"> + <font color="white"> </font> + </para> + <blockTable colWidths="510.0" style="Tableau4"> + <tr> + <td> + <para style="P25">[[ o.notes or '' ]] [[ setTag('para','xpre') ]]</para> + </td> + </tr> + </blockTable> + <para style="P25"> + <font color="white"> </font> + </para> + </story> +</document> + diff --git a/addons/purchase/report/request_quotation.py b/addons/purchase/report/request_quotation.py new file mode 100644 index 00000000000..8f8c76318ef --- /dev/null +++ b/addons/purchase/report/request_quotation.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Copyright (c) 2006 TINY SPRL. (http://tiny.be) All Rights Reserved. +# +# WARNING: This program as such is intended to be used by professional +# programmers who take the whole responsability of assessing all potential +# consequences resulting from its eventual inadequacies and bugs +# End users who are looking for a ready-to-use solution with commercial +# garantees and support are strongly adviced to contract a Free Software +# Service Company +# +# This program is Free Software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +############################################################################## + +import time +from report import report_sxw +from osv import osv +import pooler + +class request_quotation(report_sxw.rml_parse): + def __init__(self, cr, uid, name, context): + super(request_quotation, self).__init__(cr, uid, name, context) + self.localcontext.update({ + 'time': time, + 'user': self.pool.get('res.users').browse(cr, uid, uid, context) + }) +report_sxw.report_sxw('report.purchase.quotation','purchase.order','addons/purchase/report/request_quotation.rml',parser=request_quotation) diff --git a/addons/purchase/report/request_quotation.rml b/addons/purchase/report/request_quotation.rml new file mode 100644 index 00000000000..fd63b62d76f --- /dev/null +++ b/addons/purchase/report/request_quotation.rml @@ -0,0 +1,227 @@ +<?xml version="1.0"?> +<document filename="test.pdf"> + <template pageSize="(595.0,842.0)" title="Test" author="Martin Simon" allowSplitting="20"> + <pageTemplate id="first"> + <frame id="first" x1="42.0" y1="42.0" width="511" height="758"/> + </pageTemplate> + </template> + <stylesheet> + <blockTableStyle id="Standard_Outline"> + <blockAlignment value="LEFT"/> + <blockValign value="TOP"/> + </blockTableStyle> + <blockTableStyle id="Tableau6"> + <blockAlignment value="LEFT"/> + <blockValign value="TOP"/> + </blockTableStyle> + <blockTableStyle id="Tableau10"> + <blockAlignment value="LEFT"/> + <blockValign value="TOP"/> + </blockTableStyle> + <blockTableStyle id="Tableau2"> + <blockAlignment value="LEFT"/> + <blockValign value="TOP"/> + <lineStyle kind="GRID" colorName="black"/> + <blockBackground colorName="#e6e6e6" start="0,0" stop="0,0"/> + <blockBackground colorName="#e6e6e6" start="1,0" stop="1,0"/> + <blockBackground colorName="#e6e6e6" start="2,0" stop="2,0"/> + <blockBackground colorName="#e6e6e6" start="3,0" stop="3,0"/> + </blockTableStyle> + <blockTableStyle id="Tableau3"> + <blockAlignment value="LEFT"/> + <blockValign value="TOP"/> + </blockTableStyle> + <blockTableStyle id="Tableau5"> + <blockAlignment value="LEFT"/> + <blockValign value="TOP"/> + </blockTableStyle> + <blockTableStyle id="Tableau4"> + <blockAlignment value="LEFT"/> + <blockValign value="TOP"/> + </blockTableStyle> + <initialize> + <paraStyle name="all" alignment="justify"/> + </initialize> + <paraStyle name="P1" fontName="Times-Roman" fontSize="9.0" leading="11"/> + <paraStyle name="P2" fontName="Times-BoldItalic" fontSize="6.0" leading="8"/> + <paraStyle name="P3" fontName="Times-Bold" fontSize="10.0" leading="13"/> + <paraStyle name="P4" fontName="Times-Roman" fontSize="10.0" leading="13"/> + <paraStyle name="P5" fontName="Times-Roman" fontSize="10.0" leading="13"/> + <paraStyle name="P6" fontName="Times-Roman" fontSize="10.0" leading="13"/> + <paraStyle name="P7" fontName="Times-Roman" fontSize="9.0" leading="11"/> + <paraStyle name="P8" fontName="Times-Roman" fontSize="11.0" leading="14"/> + <paraStyle name="P9" fontName="Times-Roman" fontSize="4.0" leading="5"/> + <paraStyle name="P10" fontName="Times-Roman" fontSize="16.0" leading="20"/> + <paraStyle name="P11" fontName="Times-Roman" fontSize="11.0" leading="14"/> + <paraStyle name="P12" fontName="Times-Roman" fontSize="11.0" leading="14"/> + <paraStyle name="P13" fontName="Times-Roman"/> + <paraStyle name="P14" fontName="Times-Bold" fontSize="8.0" leading="10"/> + <paraStyle name="P15" fontName="Times-Bold" fontSize="8.0" leading="10" alignment="CENTER"/> + <paraStyle name="P16" fontName="Times-BoldItalic" fontSize="6.0" leading="8" alignment="LEFT"/> + <paraStyle name="P17" fontName="Times-Roman" fontSize="9.0" leading="11" alignment="LEFT"/> + <paraStyle name="P18" fontName="Times-Roman" fontSize="9.0" leading="11" alignment="CENTER"/> + <paraStyle name="P19" fontName="Times-Roman" fontSize="9.0" leading="11" alignment="RIGHT"/> + <paraStyle name="P20" fontName="Times-BoldItalic" fontSize="9.0" leading="11" alignment="LEFT"/> + <paraStyle name="P21" fontName="Times-Italic" fontSize="9.0" leading="11" alignment="LEFT"/> + <paraStyle name="P22" fontName="Times-Bold" fontSize="9.0" leading="11"/> + <paraStyle name="P23" fontName="Times-Roman" alignment="LEFT"/> + <paraStyle name="P24" fontName="Times-Roman" fontSize="11.0" leading="14" alignment="LEFT"/> + <paraStyle name="Standard" fontName="Times-Roman"/> + <paraStyle name="Text body" fontName="Times-Roman" spaceBefore="0.0" spaceAfter="6.0"/> + <paraStyle name="Heading" fontName="Helvetica" fontSize="14.0" leading="17" spaceBefore="12.0" spaceAfter="6.0"/> + <paraStyle name="List" fontName="Times-Roman" spaceBefore="0.0" spaceAfter="6.0"/> + <paraStyle name="Table Contents" fontName="Times-Roman"/> + <paraStyle name="Table Heading" fontName="Times-Roman" alignment="CENTER"/> + <paraStyle name="Caption" fontName="Times-Roman" fontSize="12.0" leading="15" spaceBefore="6.0" spaceAfter="6.0"/> + <paraStyle name="Index" fontName="Times-Roman"/> + </stylesheet> + <story> + <para style="P1">[[ repeatIn(objects,'o') ]]</para> + <para style="P2">[[ setLang(o.partner_id.lang) ]]</para> + <blockTable colWidths="241.0,57.0,213.0" style="Tableau6"> + <tr> + <td> + <blockTable colWidths="241.0" style="Tableau10"> + <tr> + <td> + <para style="P3">Shipping address :</para> + </td> + </tr> + <tr> + <td> + <para style="P4">[[ repeatIn(o.dest_address_id and [o.dest_address_id] or [],'addr') ]]</para> + <para style="P5">[[ o.partner_id.title or '' ]] [[ o.partner_id.name ]]</para> + <para style="P5">[[ addr.title or '' ]] [[ addr.name ]]</para> + <para style="P5">[[ addr.street ]]</para> + <para style="P5">[[ addr.street2 or '' ]]</para> + <para style="P6"><font face="Times-Roman">[[ addr.zip or '' ]]</font> [[ addr.city or '' ]]</para> + <para style="P6">[[ addr.state_id and addr.state_id.name or '' ]]</para> + <para style="P6">[[ addr.country_id and addr.country_id.name or '' ]]</para> + </td> + </tr> + <tr> + <td> + <para style="P4">[[ repeatIn((o.warehouse_id and o.warehouse_id.partner_address_id and [o.warehouse_id.partner_address_id]) or [],'addr') ]]</para> + <para style="P5">[[ o.partner_id.title or '' ]] [[ o.partner_id.name ]]</para> + <para style="P5">[[ addr.title or '' ]] [[ addr.name ]]</para> + <para style="P5">[[ addr.street ]]</para> + <para style="P5">[[ addr.street2 or '' ]]</para> + <para style="P6"><font face="Times-Roman">[[ addr.zip or '' ]]</font> [[ addr.city or '' ]]</para> + <para style="P6">[[ addr.state_id and addr.state_id.name or '' ]]</para> + <para style="P4">[[ addr.country_id and addr.country_id.name or '' ]]</para> + </td> + </tr> + </blockTable> + <para style="P7"> + <font color="white"> </font> + </para> + </td> + <td> + <para style="P7"> + <font color="white"> </font> + </para> + </td> + <td> + <para style="P8">[[ o.partner_id.title or '' ]] [[ o.partner_id.name ]]</para> + <para style="P8">[[ o.partner_address_id.title or '' ]] [[ o.partner_address_id.name ]]</para> + <para style="P8">[[ o.partner_address_id.street ]]</para> + <para style="P8">[[ o.partner_address_id.street2 or '' ]]</para> + <para style="P8">[[ o.partner_address_id.zip or '' ]] [[ o.partner_address_id.city or '' ]]</para> + <para style="P8">[[ o.partner_address_id.state_id and o.partner_address_id.state_id.name or '' ]]</para> + <para style="P8">[[ o.partner_address_id.country_id and o.partner_address_id.country_id.name or '' ]]</para> + <para style="P9"> + <font color="white"> </font> + </para> + <para style="P5">Tél. : [[ o.partner_address_id.phone or '' ]]</para> + <para style="P5">Fax : [[ o.partner_address_id.fax or '' ]]</para> + <para style="P5">TVA : [[ o.partner_id.vat or '' ]]</para> + </td> + </tr> + </blockTable> + <para style="P10">Request for Quotation [[ o.name ]]</para> + <para style="P11">Other reference : [[ o.ref or '' ]]</para> + <para style="P12">Printed: [[ time.strftime('%d/%m/%Y') ]]</para> + <para style="P12"> + <font color="white"> </font> + </para> + <para style="P11">Please find hereby a request for quotation.</para> + <para style="P13"> + <font color="white"> </font> + </para> + <blockTable colWidths="96.0,262.0,70.0,82.0" repeatRows="1" style="Tableau2"> + <tr> + <td> + <para style="P14">Code</para> + </td> + <td> + <para style="P14">Product</para> + </td> + <td> + <para style="P15">Date</para> + </td> + <td> + <para style="P15">Qty</para> + </td> + </tr> + </blockTable> + <section> + <para style="P16">[[ repeatIn(o.order_line,'line') ]]</para> + <blockTable colWidths="96.0,262.0,70.0,82.0" style="Tableau3"> + <tr> + <td> + <para style="P17">[[ line.product_id and line.product_id.default_code or '' ]]</para> + </td> + <td> + <para style="P17">[[ line.name ]]</para> + </td> + <td> + <para style="P18">[[ time.strftime('%d/%m/%Y', time.strptime( line.date_planned, '%Y-%m-%d')) ]]</para> + </td> + <td> + <para style="P19">[[ line.product_qty ]] [[ line.product_uom.name ]]</para> + </td> + </tr> + </blockTable> + <blockTable colWidths="96.0,332.0,82.0" style="Tableau5"> + <tr> + <td> + <para style="P20">[[ repeatIn((line.notes and [line.notes]) or [], 'l') ]]</para> + </td> + <td> + <para style="P21">[[ l or removeParentNode('table') ]]</para> + </td> + <td> + <para style="P20"> + <font color="white"> </font> + </para> + </td> + </tr> + </blockTable> + </section> + <para style="P22"> + <font color="white"> </font> + </para> + <blockTable colWidths="510.0" style="Tableau4"> + <tr> + <td> + <para style="P23">[[ o.notes or '' ]] [[ setTag('para','xpre') ]]</para> + </td> + </tr> + </blockTable> + <para style="P11"> + <font color="white"> </font> + </para> + <para style="P11">Thanks,</para> + <para style="P11"> + <font color="white"> </font> + </para> + <para style="P11">[[ user.name ]]</para> + <para style="P11">Tel: [[ user.address_id.phone ]]</para> + <para style="P11">Email: [[ user.address_id.email ]]</para> + <para style="P11">Fax: [[ user.address_id.fax ]]</para> + <para style="P24"> + <font color="white"> </font> + </para> + </story> +</document> + diff --git a/addons/purchase/wizard/__init__.py b/addons/purchase/wizard/__init__.py new file mode 100644 index 00000000000..3577c3e19f9 --- /dev/null +++ b/addons/purchase/wizard/__init__.py @@ -0,0 +1,28 @@ +############################################################################## +# +# Copyright (c) 2006 TINY SPRL. (http://tiny.be) All Rights Reserved. +# +# WARNING: This program as such is intended to be used by professional +# programmers who take the whole responsability of assessing all potential +# consequences resulting from its eventual inadequacies and bugs +# End users who are looking for a ready-to-use solution with commercial +# garantees and support are strongly adviced to contract a Free Software +# Service Company +# +# This program is Free Software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +############################################################################## + +import wizard_group diff --git a/addons/purchase/wizard/wizard_group.py b/addons/purchase/wizard/wizard_group.py new file mode 100644 index 00000000000..8ebca0e223b --- /dev/null +++ b/addons/purchase/wizard/wizard_group.py @@ -0,0 +1,156 @@ +############################################################################## +# +# Copyright (c) 2006 TINY SPRL. (http://tiny.be) All Rights Reserved. +# +# $Id$ +# +# WARNING: This program as such is intended to be used by professional +# programmers who take the whole responsability of assessing all potential +# consequences resulting from its eventual inadequacies and bugs +# End users who are looking for a ready-to-use solution with commercial +# garantees and support are strongly adviced to contract a Free Software +# Service Company +# +# This program is Free Software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +############################################################################## + +import time + +import wizard +import netsvc +import pooler +from osv.orm import browse_record + +merge_form = """<?xml version="1.0"?> +<form string="Merge orders"> + <separator string="Are you sure you want to merge these orders ?"/> + <newline/> + <label string="Please note that orders will only be merged if they are to the same partner and going to the same location, and lines will only be merged if they are exactly the same except for the quantity and unit."/> +</form> +""" + +merge_fields = { +} + +ack_form = """<?xml version="1.0"?> +<form string="Merge orders"> + <separator string="Orders merged"/> +</form>""" + +ack_fields = {} + +def _mergeOrders(self, cr, uid, data, context): + order_obj = pooler.get_pool(cr.dbname).get('purchase.order') + + def make_key(br, fields): + list_key = [] + for field in fields: + field_val = getattr(br, field) + if isinstance(field_val, browse_record): + field_val = field_val.id + elif isinstance(field_val, list): + field_val = ((6, 0, tuple([v.id for v in field_val])),) + list_key.append((field, field_val)) + list_key.sort() + return tuple(list_key) + + # compute what the new orders should contain + new_orders = {} + for porder in [order for order in order_obj.browse(cr, uid, data['ids']) if order.state == 'draft']: + order_key = make_key(porder, ('partner_id', 'location_id')) + new_order = new_orders.setdefault(order_key, ({}, [])) + new_order[1].append(porder.id) + order_infos = new_order[0] + if not order_infos: + order_infos.update({ + 'origin': porder.origin, + 'date_order': time.strftime('%Y-%m-%d'), + 'partner_id': porder.partner_id.id, + 'partner_address_id': porder.partner_address_id.id, + 'dest_address_id': porder.dest_address_id.id, + 'warehouse_id': porder.warehouse_id.id, + 'location_id': porder.location_id.id, + 'project_id': porder.project_id.id, + 'pricelist_id': porder.pricelist_id.id, + 'state': 'draft', + 'order_line': {}, + 'notes': '%s' % (porder.notes,), + }) + else: + #order_infos['name'] += ', %s' % porder.name + order_infos['notes'] += ('\n%s' % (porder.notes,)) + + for order_line in porder.order_line: + line_key = make_key(order_line, ('name', 'date_planned', 'taxes_id', 'price_unit', 'notes', 'product_id', 'move_dest_id')) + o_line = order_infos['order_line'].setdefault(line_key, {}) + if o_line: + # merge the line with an existing line + o_line['product_qty'] += order_line.product_qty * order_line.product_uom.factor / o_line['uom_factor'] + else: + # append a new "standalone" line + for field in ('product_qty', 'product_uom'): + field_val = getattr(order_line, field) + if isinstance(field_val, browse_record): + field_val = field_val.id + o_line[field] = field_val + o_line['uom_factor'] = order_line.product_uom and order_line.product_uom.factor or 1.0 + + wf_service = netsvc.LocalService("workflow") + + allorders = [] + for order_key, (order_data, old_ids) in new_orders.iteritems(): + # skip merges with only one order + if len(old_ids) < 2: + allorders += (old_ids or []) + continue + + # cleanup order line data + for key, value in order_data['order_line'].iteritems(): + del value['uom_factor'] + value.update(dict(key)) + order_data['order_line'] = [(0, 0, value) for value in order_data['order_line'].itervalues()] + + # create the new order + neworder_id = order_obj.create(cr, uid, order_data) + allorders.append(neworder_id) + + # make triggers pointing to the old orders point to the new order + for old_id in old_ids: + wf_service.trg_redirect(uid, 'purchase.order', old_id, neworder_id, cr) + wf_service.trg_validate(uid, 'purchase.order', old_id, 'purchase_cancel', cr) + + return { + 'domain': "[('id','in', ["+','.join(map(str,allorders))+"])]", + 'name': 'Purchase Orders', + 'view_type': 'form', + 'view_mode': 'tree,form', + 'res_model': 'purchase.order', + 'view_id': False, + 'type': 'ir.actions.act_window' + } + +class merge_orders(wizard.interface): + states = { + 'init' : { + 'actions' : [], + 'result' : {'type' : 'form', 'arch' : merge_form, 'fields' : merge_fields, 'state' : [('end', 'Cancel'),('merge', 'Merge orders') ]} + }, + 'merge' : { + 'actions' : [], + 'result' : {'type':'action', 'action':_mergeOrders, 'state':'end'} + }, + } +merge_orders("purchase.order.merge") |
