+# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
_order = 'parent_left'
def name_get(self, cr, uid, ids, context=None):
- res = []
- if context is None:
- context = {}
- if not len(ids):
- return []
- reads = self.read(cr, uid, ids, ['name','location_id'], context=context)
- for record in reads:
- name = record['name']
- if context.get('full',False):
- if record['location_id']:
- name = record['location_id'][1] + ' / ' + name
- res.append((record['id'], name))
- else:
- res.append((record['id'], name))
- return res
+ # always return the full hierarchical name
+ res = self._complete_name(cr, uid, ids, 'complete_name', None, context=context)
+ return res.items()
def _complete_name(self, cr, uid, ids, name, args, context=None):
""" Forms complete name of location from parent location to child location.
@return: Dictionary of values
"""
- def _get_one_full_name(location, level=4):
- if location.location_id:
- parent_path = _get_one_full_name(location.location_id, level-1) + "/"
- else:
- parent_path = ''
- return parent_path + location.name
res = {}
for m in self.browse(cr, uid, ids, context=context):
- res[m.id] = _get_one_full_name(m)
+ names = [m.name]
+ parent = m.location_id
+ while parent:
+ names.append(parent.name)
+ parent = parent.location_id
+ res[m.id] = ' / '.join(reversed(names))
return res
+ def _get_sublocations(self, cr, uid, ids, context=None):
+ """ return all sublocations of the given stock locations (included) """
+ return self.search(cr, uid, [('id', 'child_of', ids)], context=context)
def _product_value(self, cr, uid, ids, field_names, arg, context=None):
"""Computes stock value (real and virtual) for a product, as well as stock qty (real and virtual).
\n* Production: Virtual counterpart location for production operations: this location consumes the raw material and produces finished products
""", select = True),
# temporarily removed, as it's unused: 'allocation_method': fields.selection([('fifo', 'FIFO'), ('lifo', 'LIFO'), ('nearest', 'Nearest')], 'Allocation Method', required=True),
- 'complete_name': fields.function(_complete_name, type='char', size=256, string="Location Name", store=True),
+ 'complete_name': fields.function(_complete_name, type='char', size=256, string="Location Name",
+ store={'stock.location': (_get_sublocations, ['name', 'location_id'], 10)}),
'stock_real': fields.function(_product_value, type='float', string='Real Stock', multi="stock"),
'stock_virtual': fields.function(_product_value, type='float', string='Virtual Stock', multi="stock"),
""",
(id, id, product_id))
results += cr.dictfetchall()
-
total = 0.0
results2 = 0.0
for r in results:
amount = uom_obj._compute_qty(cr, uid, r['product_uom'], r['product_qty'], context.get('uom', False))
results2 += amount
total += amount
-
if total <= 0.0:
continue
@param context: A standard dictionary
@return: A dictionary of values
"""
- value={}
- value=self.pool.get('action.traceability').action_traceability(cr,uid,ids,context)
- return value
+ return self.pool.get('action.traceability').action_traceability(cr,uid,ids,context)
+
stock_tracking()
#----------------------------------------------------------
""" Tests whether the move is in assigned state or not.
@return: True or False
"""
+ #TOFIX: assignment of move lines should be call before testing assigment otherwise picking never gone in assign state
ok = True
for pick in self.browse(cr, uid, ids):
mt = pick.move_type
def get_currency_id(self, cr, uid, picking):
return False
- def _get_payment_term(self, cr, uid, picking):
- """ Gets payment term from partner.
- @return: Payment term
+ def _get_partner_to_invoice(self, cr, uid, picking, context=None):
+ """ Gets the partner that will be invoiced
+ Note that this function is inherited in the sale module
+ @param picking: object of the picking for which we are selecting the partner to invoice
+ @return: object of the partner to invoice
"""
- partner = picking.address_id.partner_id
- return partner.property_payment_term and partner.property_payment_term.id or False
-
- def _get_address_invoice(self, cr, uid, picking):
- """ Gets invoice address of a partner
- @return {'contact': address, 'invoice': address} for invoice
- """
- partner_obj = self.pool.get('res.partner')
- partner = picking.address_id.partner_id
- return partner_obj.address_get(cr, uid, [partner.id],
- ['contact', 'invoice'])
+ return picking.address_id and picking.address_id.partner_id
def _get_comment_invoice(self, cr, uid, picking):
"""
inv_type = 'out_invoice'
return inv_type
+ def _prepare_invoice_group(self, cr, uid, picking, partner, invoice, context=None):
+ """ Builds the dict for grouped invoices
+ @param picking: picking object
+ @param partner: object of the partner to invoice (not used here, but may be usefull if this function is inherited)
+ @param invoice: object of the invoice that we are updating
+ @return: dict that will be used to update the invoice
+ """
+ comment = self._get_comment_invoice(cr, uid, picking)
+ return {
+ 'name': (invoice.name or '') + ', ' + (picking.name or ''),
+ 'origin': (invoice.origin or '') + ', ' + (picking.name or '') + (picking.origin and (':' + picking.origin) or ''),
+ 'comment': (comment and (invoice.comment and invoice.comment + "\n" + comment or comment)) or (invoice.comment and invoice.comment or ''),
+ 'date_invoice': context.get('date_inv', False),
+ 'user_id': uid,
+ }
+
+ def _prepare_invoice(self, cr, uid, picking, partner, inv_type, journal_id, context=None):
+ """ Builds the dict containing the values for the invoice
+ @param picking: picking object
+ @param partner: object of the partner to invoice
+ @param inv_type: type of the invoice ('out_invoice', 'in_invoice', ...)
+ @param journal_id: ID of the accounting journal
+ @return: dict that will be used to create the invoice object
+ """
+ if inv_type in ('out_invoice', 'out_refund'):
+ account_id = partner.property_account_receivable.id
+ else:
+ account_id = partner.property_account_payable.id
+ address_contact_id, address_invoice_id = \
+ self.pool.get('res.partner').address_get(cr, uid, [partner.id],
+ ['contact', 'invoice']).values()
+ comment = self._get_comment_invoice(cr, uid, picking)
+ invoice_vals = {
+ 'name': picking.name,
+ 'origin': (picking.name or '') + (picking.origin and (':' + picking.origin) or ''),
+ 'type': inv_type,
+ 'account_id': account_id,
+ 'partner_id': partner.id,
+ 'address_invoice_id': address_invoice_id,
+ 'address_contact_id': address_contact_id,
+ 'comment': comment,
+ 'payment_term': partner.property_payment_term and partner.property_payment_term.id or False,
+ 'fiscal_position': partner.property_account_position.id,
+ 'date_invoice': context.get('date_inv', False),
+ 'company_id': picking.company_id.id,
+ 'user_id': uid,
+ }
+ cur_id = self.get_currency_id(cr, uid, picking)
+ if cur_id:
+ invoice_vals['currency_id'] = cur_id
+ if journal_id:
+ invoice_vals['journal_id'] = journal_id
+ return invoice_vals
+
+ def _prepare_invoice_line(self, cr, uid, group, picking, move_line, invoice_id,
+ invoice_vals, context=None):
+ """ Builds the dict containing the values for the invoice line
+ @param group: True or False
+ @param picking: picking object
+ @param: move_line: move_line object
+ @param: invoice_id: ID of the related invoice
+ @param: invoice_vals: dict used to created the invoice
+ @return: dict that will be used to create the invoice line
+ """
+ if group:
+ name = (picking.name or '') + '-' + move_line.name
+ else:
+ name = move_line.name
+ origin = move_line.picking_id.name or ''
+ if move_line.picking_id.origin:
+ origin += ':' + move_line.picking_id.origin
+
+ if invoice_vals['type'] in ('out_invoice', 'out_refund'):
+ account_id = move_line.product_id.product_tmpl_id.\
+ property_account_income.id
+ if not account_id:
+ account_id = move_line.product_id.categ_id.\
+ property_account_income_categ.id
+ else:
+ account_id = move_line.product_id.product_tmpl_id.\
+ property_account_expense.id
+ if not account_id:
+ account_id = move_line.product_id.categ_id.\
+ property_account_expense_categ.id
+ if invoice_vals['fiscal_position']:
+ fp_obj = self.pool.get('account.fiscal.position')
+ fiscal_position = fp_obj.browse(cr, uid, invoice_vals['fiscal_position'], context=context)
+ account_id = fp_obj.map_account(cr, uid, fiscal_position, account_id)
+ # set UoS if it's a sale and the picking doesn't have one
+ uos_id = move_line.product_uos and move_line.product_uos.id or False
+ if not uos_id and invoice_vals['type'] in ('out_invoice', 'out_refund'):
+ uos_id = move_line.product_uom.id
+
+ return {
+ 'name': name,
+ 'origin': origin,
+ 'invoice_id': invoice_id,
+ 'uos_id': uos_id,
+ 'product_id': move_line.product_id.id,
+ 'account_id': account_id,
+ 'price_unit': self._get_price_unit_invoice(cr, uid, move_line, invoice_vals['type']),
+ 'discount': self._get_discount_invoice(cr, uid, move_line),
+ 'quantity': move_line.product_uos_qty or move_line.product_qty,
+ 'invoice_line_tax_id': [(6, 0, self._get_taxes_invoice(cr, uid, move_line, invoice_vals['type']))],
+ 'account_analytic_id': self._get_account_analytic_invoice(cr, uid, picking, move_line),
+ }
+
def action_invoice_create(self, cr, uid, ids, journal_id=False,
group=False, type='out_invoice', context=None):
""" Creates invoice based on the invoice state selected for picking.
invoice_obj = self.pool.get('account.invoice')
invoice_line_obj = self.pool.get('account.invoice.line')
- address_obj = self.pool.get('res.partner.address')
invoices_group = {}
res = {}
inv_type = type
for picking in self.browse(cr, uid, ids, context=context):
if picking.invoice_state != '2binvoiced':
continue
- partner = picking.address_id and picking.address_id.partner_id
+ partner = self._get_partner_to_invoice(cr, uid, picking, context=context)
if not partner:
raise osv.except_osv(_('Error, no partner !'),
_('Please put a partner on the picking list if you want to generate invoice.'))
if not inv_type:
inv_type = self._get_invoice_type(picking)
- if inv_type in ('out_invoice', 'out_refund'):
- account_id = partner.property_account_receivable.id
- else:
- account_id = partner.property_account_payable.id
- address_contact_id, address_invoice_id = \
- self._get_address_invoice(cr, uid, picking).values()
- address = address_obj.browse(cr, uid, address_contact_id, context=context)
-
- comment = self._get_comment_invoice(cr, uid, picking)
if group and partner.id in invoices_group:
invoice_id = invoices_group[partner.id]
invoice = invoice_obj.browse(cr, uid, invoice_id)
- invoice_vals = {
- 'name': (invoice.name or '') + ', ' + (picking.name or ''),
- 'origin': (invoice.origin or '') + ', ' + (picking.name or '') + (picking.origin and (':' + picking.origin) or ''),
- 'comment': (comment and (invoice.comment and invoice.comment+"\n"+comment or comment)) or (invoice.comment and invoice.comment or ''),
- 'date_invoice':context.get('date_inv',False),
- 'user_id':uid
- }
- invoice_obj.write(cr, uid, [invoice_id], invoice_vals, context=context)
+ invoice_vals_group = self._prepare_invoice_group(cr, uid, picking, partner, invoice, context=context)
+ invoice_obj.write(cr, uid, [invoice_id], invoice_vals_group, context=context)
else:
- invoice_vals = {
- 'name': picking.name,
- 'origin': (picking.name or '') + (picking.origin and (':' + picking.origin) or ''),
- 'type': inv_type,
- 'account_id': account_id,
- 'partner_id': address.partner_id.id,
- 'address_invoice_id': address_invoice_id,
- 'address_contact_id': address_contact_id,
- 'comment': comment,
- 'payment_term': self._get_payment_term(cr, uid, picking),
- 'fiscal_position': partner.property_account_position.id,
- 'date_invoice': context.get('date_inv',False),
- 'company_id': picking.company_id.id,
- 'user_id':uid
- }
- cur_id = self.get_currency_id(cr, uid, picking)
- if cur_id:
- invoice_vals['currency_id'] = cur_id
- if journal_id:
- invoice_vals['journal_id'] = journal_id
- invoice_id = invoice_obj.create(cr, uid, invoice_vals,
- context=context)
+ invoice_vals = self._prepare_invoice(cr, uid, picking, partner, inv_type, journal_id, context=context)
+ invoice_id = invoice_obj.create(cr, uid, invoice_vals, context=context)
invoices_group[partner.id] = invoice_id
res[picking.id] = invoice_id
for move_line in picking.move_lines:
if move_line.state == 'cancel':
continue
- origin = move_line.picking_id.name or ''
- if move_line.picking_id.origin:
- origin += ':' + move_line.picking_id.origin
- if group:
- name = (picking.name or '') + '-' + move_line.name
- else:
- name = move_line.name
-
- if inv_type in ('out_invoice', 'out_refund'):
- account_id = move_line.product_id.product_tmpl_id.\
- property_account_income.id
- if not account_id:
- account_id = move_line.product_id.categ_id.\
- property_account_income_categ.id
- else:
- account_id = move_line.product_id.product_tmpl_id.\
- property_account_expense.id
- if not account_id:
- account_id = move_line.product_id.categ_id.\
- property_account_expense_categ.id
-
- price_unit = self._get_price_unit_invoice(cr, uid,
- move_line, inv_type)
- discount = self._get_discount_invoice(cr, uid, move_line)
- tax_ids = self._get_taxes_invoice(cr, uid, move_line, inv_type)
- account_analytic_id = self._get_account_analytic_invoice(cr, uid, picking, move_line)
-
- #set UoS if it's a sale and the picking doesn't have one
- uos_id = move_line.product_uos and move_line.product_uos.id or False
- if not uos_id and inv_type in ('out_invoice', 'out_refund'):
- uos_id = move_line.product_uom.id
-
- account_id = self.pool.get('account.fiscal.position').map_account(cr, uid, partner.property_account_position, account_id)
- invoice_line_id = invoice_line_obj.create(cr, uid, {
- 'name': name,
- 'origin': origin,
- 'invoice_id': invoice_id,
- 'uos_id': uos_id,
- 'product_id': move_line.product_id.id,
- 'account_id': account_id,
- 'price_unit': price_unit,
- 'discount': discount,
- 'quantity': move_line.product_uos_qty or move_line.product_qty,
- 'invoice_line_tax_id': [(6, 0, tax_ids)],
- 'account_analytic_id': account_analytic_id,
- }, context=context)
- self._invoice_line_hook(cr, uid, move_line, invoice_line_id)
+ vals = self._prepare_invoice_line(cr, uid, group, picking, move_line,
+ invoice_id, invoice_vals, context=context)
+ if vals:
+ invoice_line_id = invoice_line_obj.create(cr, uid, vals, context=context)
+ self._invoice_line_hook(cr, uid, move_line, invoice_line_id)
invoice_obj.button_compute(cr, uid, [invoice_id], context=context,
set_total=(inv_type in ('in_invoice', 'in_refund')))
for pick in self.browse(cr, uid, ids, context=context):
new_picking = None
complete, too_many, too_few = [], [], []
- move_product_qty = {}
- prodlot_ids = {}
- product_avail = {}
+ move_product_qty, prodlot_ids, product_avail, partial_qty, product_uoms = {}, {}, {}, {}, {}
for move in pick.move_lines:
if move.state in ('done', 'cancel'):
continue
product_currency = partial_data.get('product_currency',False)
prodlot_id = partial_data.get('prodlot_id')
prodlot_ids[move.id] = prodlot_id
- if move.product_qty == product_qty:
+ product_uoms[move.id] = product_uom
+ partial_qty[move.id] = uom_obj._compute_qty(cr, uid, product_uoms[move.id], product_qty, move.product_uom.id)
+ if move.product_qty == partial_qty[move.id]:
complete.append(move)
- elif move.product_qty > product_qty:
+ elif move.product_qty > partial_qty[move.id]:
too_few.append(move)
else:
too_many.append(move)
for move in too_few:
product_qty = move_product_qty[move.id]
-
if not new_picking:
new_picking = self.copy(cr, uid, pick.id,
{
'state': 'assigned',
'move_dest_id': False,
'price_unit': move.price_unit,
+ 'product_uom': product_uoms[move.id]
}
prodlot_id = prodlot_ids[move.id]
if prodlot_id:
defaults.update(prodlot_id=prodlot_id)
move_obj.copy(cr, uid, move.id, defaults)
-
move_obj.write(cr, uid, [move.id],
{
- 'product_qty' : move.product_qty - product_qty,
- 'product_uos_qty':move.product_qty - product_qty, #TODO: put correct uos_qty
+ 'product_qty' : move.product_qty - partial_qty[move.id],
+ 'product_uos_qty': move.product_qty - partial_qty[move.id], #TODO: put correct uos_qty
+
})
if new_picking:
move_obj.write(cr, uid, [c.id for c in complete], {'picking_id': new_picking})
for move in complete:
+ defaults = {'product_uom': product_uoms[move.id], 'product_qty': move_product_qty[move.id]}
if prodlot_ids.get(move.id):
- move_obj.write(cr, uid, [move.id], {'prodlot_id': prodlot_ids[move.id]})
+ defaults.update({'prodlot_id': prodlot_ids[move.id]})
+ move_obj.write(cr, uid, [move.id], defaults)
for move in too_many:
product_qty = move_product_qty[move.id]
defaults = {
'product_qty' : product_qty,
'product_uos_qty': product_qty, #TODO: put correct uos_qty
+ 'product_uom': product_uoms[move.id]
}
prodlot_id = prodlot_ids.get(move.id)
if prodlot_ids.get(move.id):
defaults.update(picking_id=new_picking)
move_obj.write(cr, uid, [move.id], defaults)
-
# At first we confirm the new picking (if necessary)
if new_picking:
wf_service.trg_validate(uid, 'stock.picking', new_picking, 'button_confirm', cr)
_defaults = {
'author_id': lambda x, y, z, c: z,
- 'date': time.strftime('%Y-%m-%d'),
+ 'date': fields.date.context_today,
}
stock_production_lot_revision()
WHERE indexname = \'stock_move_location_id_location_dest_id_product_id_state\'')
if not cursor.fetchone():
cursor.execute('CREATE INDEX stock_move_location_id_location_dest_id_product_id_state \
- ON stock_move (location_id, location_dest_id, product_id, state)')
+ ON stock_move (product_id, state, location_id, location_dest_id)')
return res
def onchange_lot_id(self, cr, uid, ids, prodlot_id=False, product_qty=False,
@param context: context arguments
@return: Scraped lines
"""
+ #quantity should in MOVE UOM
if quantity <= 0:
raise osv.except_osv(_('Warning!'), _('Please provide a positive quantity to scrap!'))
res = []
@param context: context arguments
@return: Consumed lines
"""
+ #quantity should in MOVE UOM
if context is None:
context = {}
if quantity <= 0:
quantity = move.product_qty
uos_qty = quantity / move_qty * move.product_uos_qty
-
+ location_dest_id = move.product_id.property_stock_production or move.location_dest_id
if quantity_rest > 0:
default_val = {
'product_qty': quantity,
'product_uos_qty': uos_qty,
'state': move.state,
'location_id': location_id or move.location_id.id,
+ 'location_dest_id': location_dest_id.id,
}
current_move = self.copy(cr, uid, move.id, default_val)
res += [current_move]
update_val = {
'product_qty' : quantity_rest,
'product_uos_qty' : uos_qty_rest,
- 'location_id': location_id or move.location_id.id
+ 'location_id': location_id or move.location_id.id,
+ 'location_dest_id': location_dest_id.id,
}
self.write(cr, uid, [move.id], update_val)
'company_id': lambda self,cr,uid,c: self.pool.get('res.company')._company_default_get(cr, uid, 'stock.inventory', context=c)
}
+ def copy(self, cr, uid, id, default=None, context=None):
+ if default is None:
+ default = {}
+ default = default.copy()
+ default.update({'move_ids': [], 'date_done': False})
+ return super(stock_inventory, self).copy(cr, uid, id, default, context=context)
+
def _inventory_line_hook(self, cr, uid, inventory_line, move_vals):
""" Creates a stock move from an inventory line
@param inventory_line:
message = _("Inventory '%s' is done.") %(inv.name)
self.log(cr, uid, inv.id, message)
self.write(cr, uid, [inv.id], {'state': 'confirm', 'move_ids': [(6, 0, move_ids)]})
+ self.pool.get('stock.move').action_confirm(cr, uid, move_ids, context=context)
return True
def action_cancel_draft(self, cr, uid, ids, context=None):
self.write(cr, uid, [inv.id], {'state':'draft'}, context=context)
return True
- def action_cancel_inventary(self, cr, uid, ids, context=None):
+ def action_cancel_inventory(self, cr, uid, ids, context=None):
""" Cancels both stock move and inventory
@return: True
"""