diff --git a/addons/account_voucher/account_voucher.py b/addons/account_voucher/account_voucher.py
index a07e41ff000..6b8e0276610 100644
--- a/addons/account_voucher/account_voucher.py
+++ b/addons/account_voucher/account_voucher.py
@@ -501,6 +501,8 @@ class account_voucher(osv.osv):
def proforma_voucher(self, cr, uid, ids, context=None):
self.action_move_line_create(cr, uid, ids, context=context)
return True
+
+
def action_cancel_draft(self, cr, uid, ids, context={}):
wf_service = netsvc.LocalService("workflow")
diff --git a/addons/purchase/purchase.py b/addons/purchase/purchase.py
index da455015d3c..3145979d7b0 100644
--- a/addons/purchase/purchase.py
+++ b/addons/purchase/purchase.py
@@ -187,7 +187,7 @@ class purchase_order(osv.osv):
help="From Order: a draft invoice will be pre-generated based on the purchase order. The accountant " \
"will just have to validate this invoice for control.\n" \
"From Picking: a draft invoice will be pre-generated based on validated receptions.\n" \
- "Manual: no invoice will be pre-generated. The accountant will have to encode manually."
+ "Manual: allows you to generate suppliers invoices by chosing in the uninvoiced lines of all manual purchase orders."
),
'minimum_planned_date':fields.function(_minimum_planned_date, fnct_inv=_set_minimum_planned_date, method=True,store=True, string='Expected Date', type='date', help="This is computed as the minimum scheduled date of all purchase order lines' products."),
'amount_untaxed': fields.function(_amount_all, method=True, digits_compute= dp.get_precision('Purchase Price'), string='Untaxed Amount',
@@ -597,7 +597,7 @@ class purchase_order_line(osv.osv):
_columns = {
'name': fields.char('Description', size=256, required=True),
'product_qty': fields.float('Quantity', required=True, digits=(16,2)),
- 'date_planned': fields.date('Scheduled date', required=True),
+ 'date_planned': fields.date('Scheduled Date', 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),
@@ -616,7 +616,7 @@ class purchase_order_line(osv.osv):
\n* The \'Cancelled\' state is set automatically when user cancel purchase order.'),
'invoice_lines': fields.many2many('account.invoice.line', 'purchase_order_line_invoice_rel', 'order_line_id', 'invoice_id', 'Invoice Lines', readonly=True),
'invoiced': fields.boolean('Invoiced', readonly=True),
- 'partner_id': fields.related('order_id','partner_id',string='Partner',readonly=True,type="many2one", relation="res.partner"),
+ 'partner_id': fields.related('order_id','partner_id',string='Partner',readonly=True,type="many2one", relation="res.partner", store=True),
'date_order': fields.related('order_id','date_order',string='Order Date',readonly=True,type="date")
}
diff --git a/addons/purchase/purchase_view.xml b/addons/purchase/purchase_view.xml
index e9e20c952b2..96f19c8d711 100644
--- a/addons/purchase/purchase_view.xml
+++ b/addons/purchase/purchase_view.xml
@@ -144,10 +144,14 @@
-
+
+<<<<<<< TREE
+=======
+
+>>>>>>> MERGE-SOURCE
@@ -307,12 +311,16 @@
-
+
-
-
+
+
+
+
+
+
@@ -369,10 +377,12 @@
-
+
+
+
@@ -401,7 +411,7 @@
Purchase Lines Not Invoiced
ir.actions.act_window
purchase.order.line
- [('state','in',('confirmed','done')), ('invoiced','=',False)]
+ [('state','in',('confirmed','done')), ('invoiced', '=', False)]
form
tree,form
diff --git a/addons/purchase/wizard/purchase_line_invoice.py b/addons/purchase/wizard/purchase_line_invoice.py
index 421f9d3185e..f57ae82e51f 100644
--- a/addons/purchase/wizard/purchase_line_invoice.py
+++ b/addons/purchase/wizard/purchase_line_invoice.py
@@ -24,6 +24,7 @@ import ir
from osv import osv
from tools.translate import _
+
class purchase_line_invoice(osv.osv_memory):
""" To create invoice for purchase order line"""
@@ -31,7 +32,8 @@ class purchase_line_invoice(osv.osv_memory):
_name = 'purchase.order.line_invoice'
_description = 'Purchase Order Line Make Invoice'
- def makeInvoices(self, cr, uid, ids, context):
+ def makeInvoices(self, cr, uid, ids, context=None):
+
"""
To get Purchase Order line and create Invoice
@param self: The object pointer.
@@ -39,7 +41,11 @@ class purchase_line_invoice(osv.osv_memory):
@param uid: ID of the user currently logged in
@param context: A standard dictionary
@return : retrun view of Invoice
- """
+ """
+
+ if context is None:
+ context={}
+
record_ids = context.get('active_ids',[])
if record_ids:
res = False
@@ -49,35 +55,63 @@ class purchase_line_invoice(osv.osv_memory):
property_obj=self.pool.get('ir.property')
account_fiscal_obj=self.pool.get('account.fiscal.position')
invoice_line_obj=self.pool.get('account.invoice.line')
-
- def make_invoice(order, lines):
- a = order.partner_id.property_account_payable.id
- if order.partner_id and order.partner_id.property_payment_term.id:
- pay_term = order.partner_id.property_payment_term.id
+
+
+ def multiple_order_invoice_name(orders):
+ name = "PO";
+ for order in orders:
+ name += "-%d" % order.id
+ return name
+
+ def multiple_order_invoice_reference(partner, orders):
+ reference = "P%dPO" % partner.id
+ for order in orders:
+ reference += "-%d" % order.id
+ return reference
+
+ def multiple_order_invoice_notes(orders):
+ notes = ""
+ for order in orders:
+ notes += "%s \n" % order.notes
+ return notes
+
+
+
+ def make_invoice_by_partner(partner, orders, lines_ids):
+ """
+ create a new invoice for one supplier
+ @param partner : The object partner
+ @param orders : The set of orders to add in the invoice
+ @param lines : The list of line's id
+ """
+
+ a = partner.property_account_payable.id
+ if partner and partner.property_payment_term.id:
+ pay_term = partner.property_payment_term.id
else:
pay_term = False
inv = {
- 'name': order.name,
- 'origin': order.name,
+ 'name': multiple_order_invoice_name(orders),
+ 'origin': multiple_order_invoice_name(orders),
'type': 'in_invoice',
- 'reference': "P%dPO%d" % (order.partner_id.id, order.id),
+ 'reference': multiple_order_invoice_reference(partner, orders),
'account_id': a,
- 'partner_id': order.partner_id.id,
- 'address_invoice_id': order.partner_address_id.id,
- 'address_contact_id': order.partner_address_id.id,
- 'invoice_line': [(6,0,lines)],
- 'currency_id' : order.pricelist_id.currency_id.id,
- 'comment': order.notes,
+ 'partner_id': partner.id,
+ 'address_invoice_id': orders[0].partner_address_id.id,
+ 'address_contact_id': orders[0].partner_address_id.id,
+ 'invoice_line': [(6,0,lines_ids)],
+ 'currency_id' : orders[0].pricelist_id.currency_id.id,
+ 'comment': multiple_order_invoice_notes(orders),
'payment_term': pay_term,
- 'fiscal_position': order.partner_id.property_account_position.id
+ 'fiscal_position': partner.property_account_position.id
}
inv_id = invoice_obj.create(cr, uid, inv)
return inv_id
for line in purchase_line_obj.browse(cr,uid,record_ids):
if (not line.invoiced) and (line.state not in ('draft','cancel')):
- if not line.order_id.id in invoices:
- invoices[line.order_id.id] = []
+ if not line.partner_id.id in invoices:
+ invoices[line.partner_id.id] = []
if line.product_id:
a = line.product_id.product_tmpl_id.property_account_expense.id
if not a:
@@ -102,18 +136,19 @@ class purchase_line_invoice(osv.osv_memory):
'uos_id': line.product_uom.id,
'product_id': line.product_id.id or False,
'invoice_line_tax_id': [(6, 0, [x.id for x in line.taxes_id])],
- 'note': line.notes,
+ 'note': line.notes,
'account_analytic_id': line.account_analytic_id and line.account_analytic_id.id or False,
})
cr.execute('insert into purchase_order_line_invoice_rel (order_line_id,invoice_id) values (%s,%s)', (line.id, inv_id))
purchase_line_obj.write(cr, uid, [line.id], {'invoiced': True})
- invoices[line.order_id.id].append((line,inv_id))
+ invoices[line.partner_id.id].append((line,inv_id))
res = []
for result in invoices.values():
- order = result[0][0].order_id
il = map(lambda x: x[1], result)
- res.append(make_invoice(order, il))
+ orders = list(set(map(lambda x : x[0].order_id, result)))
+
+ res.append(make_invoice_by_partner(orders[0].partner_id, orders, il))
return {
'domain': "[('id','in', ["+','.join(map(str,res))+"])]",
diff --git a/addons/sale/sale_view.xml b/addons/sale/sale_view.xml
index d6b1fd18449..a96fb1bc486 100644
--- a/addons/sale/sale_view.xml
+++ b/addons/sale/sale_view.xml
@@ -210,7 +210,7 @@
-
+
diff --git a/addons/stock/stock_view.xml b/addons/stock/stock_view.xml
index 9e3de5fca7c..26a3fbb7ce6 100644
--- a/addons/stock/stock_view.xml
+++ b/addons/stock/stock_view.xml
@@ -1461,12 +1461,15 @@
====================================
Reception Picking (By Stock Move)
====================================
-
+
+
+
+
stock.move.tree2
stock.move
tree
-
+
@@ -1521,6 +1524,7 @@
+
stock.move.form2
@@ -1672,7 +1676,7 @@
tree,form
['|','&',('picking_id','=',False),('location_id.usage', 'in', ['customer','supplier']),'&',('picking_id','!=',False),('picking_id.type','=','in')]
-
+
You will find here the list of all products you are waiting for, according to your preceding purchase orders. Once you receive an order, you can filter based on the name of the supplier or the purchase order reference. Then you can confirm all products received using the buttons on the right of each line.
diff --git a/addons/stock/wizard/stock_partial_move.py b/addons/stock/wizard/stock_partial_move.py
index a8327e50d0f..ba39f172f22 100644
--- a/addons/stock/wizard/stock_partial_move.py
+++ b/addons/stock/wizard/stock_partial_move.py
@@ -23,164 +23,124 @@ from osv import fields, osv
from tools.translate import _
import time
+
+class stock_partial_move_memory(osv.osv_memory):
+ _name = "stock.move.memory"
+ _rec_name = 'product_id'
+ _columns = {
+ 'product_id' : fields.many2one('product.product', string="Product", required=True, readonly=True),
+ 'quantity' : fields.float("Quantity", required=True),
+ 'product_uom': fields.many2one('product.uom', 'Unit of Measure', required=True, readonly=True),
+ 'prodlot_id' : fields.many2one('stock.production.lot', 'Production Lot'),
+ 'move_id' : fields.many2one('stock.move', "Move"),
+ 'wizard_id' : fields.many2one('stock.partial.move', string="Wizard"),
+ 'cost' : fields.float("Cost", help="Unit Cost for this product line", readonly=True),
+ 'currency' : fields.many2one('res.currency', string="Currency", help="Currency in which Unit cost is expressed", readonly=True),
+ }
+
class stock_partial_move(osv.osv_memory):
_name = "stock.partial.move"
_description = "Partial Move"
_columns = {
'date': fields.datetime('Date', required=True),
'type': fields.char("Type", size=3),
+ 'product_moves' : fields.one2many('stock.move.memory', 'wizard_id', 'Moves'),
}
-
+
def view_init(self, cr, uid, fields_list, context=None):
res = super(stock_partial_move, self).view_init(cr, uid, fields_list, context=context)
move_obj = self.pool.get('stock.move')
+
if not context:
- context={}
- for m in move_obj.browse(cr, uid, context.get('active_ids', [])):
- if m.state in ('done', 'cancel'):
+ context = {}
+ for move in move_obj.browse(cr, uid, context.get('active_ids', [])):
+ if move.state in ('done', 'cancel'):
raise osv.except_osv(_('Invalid action !'), _('Cannot deliver products which are already delivered !'))
-
- if 'move%s_product_id'%(m.id) not in self._columns:
- self._columns['move%s_product_id'%(m.id)] = fields.many2one('product.product',string="Product")
- if 'move%s_product_qty'%(m.id) not in self._columns:
- self._columns['move%s_product_qty'%(m.id)] = fields.float("Quantity")
- if 'move%s_product_uom'%(m.id) not in self._columns:
- self._columns['move%s_product_uom'%(m.id)] = fields.many2one('product.uom',string="Product UOM")
- if 'move%s_prodlot_id'%(m.id) not in self._columns:
- self._columns['move%s_prodlot_id'%(m.id)] = fields.many2one('stock.production.lot', string="Lot")
-
- if (m.picking_id.type == 'in') and (m.product_id.cost_method == 'average'):
- if 'move%s_product_price'%(m.id) not in self._columns:
- self._columns['move%s_product_price'%(m.id)] = fields.float("Cost", help="Unit Cost for this product line")
- if 'move%s_product_currency'%(m.id) not in self._columns:
- self._columns['move%s_product_currency'%(m.id)] = fields.many2one('res.currency', string="Currency", help="Currency in which Unit cost is expressed")
+
return res
+
+
+ def __create_partial_move_memory(self, move):
+ move_memory = {
+ 'product_id' : move.product_id.id,
+ 'quantity' : move.product_qty,
+ 'product_uom' : move.product_uom.id,
+ 'prodlot_id' : move.prodlot_id.id,
+ 'move_id' : move.id,
+ }
+ if move.picking_id.type == 'in':
+ move_memory.update({
+ 'cost' : move.product_id.standard_price,
+ 'currency' : move.product_id.company_id.currency_id.id,
+ })
+ return move_memory
- def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False,submenu=False):
- result = super(stock_partial_move, self).fields_view_get(cr, uid, view_id, view_type, context, toolbar,submenu)
+ def __get_active_stock_moves(self, cr, uid, context=None):
move_obj = self.pool.get('stock.move')
- move_ids = context.get('active_ids', False)
- move_ids = move_obj.search(cr, uid, [('id','in',move_ids)])
- _moves_arch_lst = """ """ % message
+
result['arch'] = _moves_arch_lst
result['fields'] = _moves_fields
return result
-
- def default_get(self, cr, uid, fields, context=None):
- """ To get default values for the object.
- @param self: The object pointer.
- @param cr: A database cursor
- @param uid: ID of the user currently logged in
- @param fields: List of fields for which we want default values
- @param context: A standard dictionary
- @return: A dictionary which of fields with values.
- """
-
- res = super(stock_partial_move, self).default_get(cr, uid, fields, context=context)
- move_obj = self.pool.get('stock.move')
- if not context:
- context={}
- if 'date' in fields:
- res.update({'date': time.strftime('%Y-%m-%d %H:%M:%S')})
- move_ids = context.get('active_ids', [])
- move_ids = move_obj.search(cr, uid, [('id','in',move_ids)])
- for m in move_obj.browse(cr, uid, context.get('active_ids', [])):
- if m.state in ('done', 'cancel'):
- continue
- res['type'] = m.picking_id and m.picking_id.type or ''
- if 'move%s_product_id'%(m.id) in fields:
- res['move%s_product_id'%(m.id)] = m.product_id.id
- if 'move%s_product_qty'%(m.id) in fields:
- res['move%s_product_qty'%(m.id)] = m.product_qty
- if 'move%s_product_uom'%(m.id) in fields:
- res['move%s_product_uom'%(m.id)] = m.product_uom.id
- if 'move%s_prodlot_id'%(m.id) in fields:
- res['move%s_prodlot_id'%(m.id)] = m.prodlot_id.id
- if m.picking_id.type == 'in' and m.product_id.cost_method == 'average':
- # Always use default product cost and currency from Product Form,
- # which belong to the Company owning the product
- currency = m.product_id.company_id.currency_id.id
- price = m.product_id.standard_price
- if 'move%s_product_price'%(m.id) in fields:
- res['move%s_product_price'%(m.id)] = price
- if 'move%s_product_currency'%(m.id) in fields:
- res['move%s_product_currency'%(m.id)] = currency
- return res
-
+
def do_partial(self, cr, uid, ids, context):
""" Makes partial moves and pickings done.
@param self: The object pointer.
@@ -190,32 +150,44 @@ class stock_partial_move(osv.osv_memory):
@param context: A standard dictionary
@return: A dictionary which of fields with values.
"""
+
move_obj = self.pool.get('stock.move')
+
move_ids = context.get('active_ids', False)
partial = self.browse(cr, uid, ids[0], context)
partial_datas = {
'delivery_date' : partial.date
}
- for m in move_obj.browse(cr, uid, move_ids):
- if m.state in ('done', 'cancel'):
+
+ p_moves = {}
+ for product_move in partial.product_moves:
+ p_moves[product_move.move_id.id] = product_move
+
+ moves_ids_final = []
+ for move in move_obj.browse(cr, uid, move_ids):
+ if move.state in ('done', 'cancel'):
continue
- partial_datas['move%s'%(m.id)] = {
- 'product_id' : getattr(partial, 'move%s_product_id'%(m.id)).id,
- 'product_qty' : getattr(partial, 'move%s_product_qty'%(m.id)),
- 'product_uom' : getattr(partial, 'move%s_product_uom'%(m.id)).id,
- 'prodlot_id' : getattr(partial, 'move%s_prodlot_id'%(m.id)).id
+ if not p_moves.get(move.id, False):
+ continue
+ partial_datas['move%s' % (move.id)] = {
+ 'product_id' : p_moves[move.id].product_id.id,
+ 'product_qty' : p_moves[move.id].quantity,
+ 'product_uom' : p_moves[move.id].product_uom.id,
+ 'prodlot_id' : p_moves[move.id].prodlot_id.id,
}
-
- if (m.picking_id.type == 'in') and (m.product_id.cost_method == 'average'):
- partial_datas['move%s'%(m.id)].update({
- 'product_price' : getattr(partial, 'move%s_product_price'%(m.id)),
- 'product_currency': getattr(partial, 'move%s_product_currency'%(m.id)).id
+ moves_ids_final.append(move.id)
+ if (move.picking_id.type == 'in') and (move.product_id.cost_method == 'average'):
+ partial_datas['move%s' % (move.id)].update({
+ 'product_price' : p_moves[move.id].cost,
+ 'product_currency': p_moves[move.id].currency.id,
})
- move_obj.do_partial(cr, uid, move_ids, partial_datas, context=context)
+
+
+ move_obj.do_partial(cr, uid, moves_ids_final, partial_datas, context=context)
return {}
stock_partial_move()
-
+stock_partial_move_memory()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/stock/wizard/stock_partial_move_view.xml b/addons/stock/wizard/stock_partial_move_view.xml
index 457ddc34ff7..2df24fc9ce8 100644
--- a/addons/stock/wizard/stock_partial_move_view.xml
+++ b/addons/stock/wizard/stock_partial_move_view.xml
@@ -2,13 +2,49 @@
-
-
+ id="action_partial_move">
+
+
+
+ stock.move.memory.tree
+ stock.move.memory
+ tree
+
+
+
+
+
+
+
+
+
+
+
+
+
+ stock.move.memory.form
+ stock.move.memory
+ form
+
+
+
+
+
+
+