#
##############################################################################
-from mx import DateTime
+from datetime import datetime
+from dateutil.relativedelta import relativedelta
+
from osv import fields, osv
from tools import config
from tools.translate import _
import time
import tools
+import decimal_precision as dp
+
#----------------------------------------------------------
# Incoterms
for r in result:
c = (context or {}).copy()
c['location'] = id
- product = self.pool.get('product.product').read(cr, uid, r['product_id'], [field_to_read, pricetype.field], context=c)
- final_value += (product[field_to_read] * product[pricetype.field])
+ product = self.pool.get('product.product').read(cr, uid, r['product_id'], [field_to_read], context=c)
+ # Compute the amount_unit in right currency
+
+ context['currency_id']=self.pool.get('res.users').browse(cr,uid,uid).company_id.currency_id.id
+ amount_unit=self.pool.get('product.product').browse(cr,uid,r['product_id']).price_get(pricetype.field, context)[r['product_id']]
+
+ final_value += (product[field_to_read] * amount_unit)
return final_value
def _product_value(self, cr, uid, ids, field_names, arg, context={}):
'stock_real_value': fields.function(_product_value, method=True, type='float', string='Real Stock Value', multi="stock"),
'stock_virtual_value': fields.function(_product_value, method=True, type='float', string='Virtual Stock Value', multi="stock"),
'company_id': fields.many2one('res.company', 'Company', required=True,select=1),
+ 'scrap_location': fields.boolean('Scrap Location', help='Check this box if the current location is a place for destroyed items'),
}
_defaults = {
'active': lambda *a: 1,
'posy': lambda *a: 0,
'posz': lambda *a: 0,
'icon': lambda *a: False,
+ 'scrap_location': lambda *a: False,
}
def chained_location_get(self, cr, uid, location, partner=None, product=None, context={}):
# Take the user company and pricetype
price_type_id=self.pool.get('res.users').browse(cr,uid,uid).company_id.property_valuation_price_type.id
pricetype=self.pool.get('product.price.type').browse(cr,uid,price_type_id)
-
+ context['currency_id']=self.pool.get('res.users').browse(cr,uid,uid).company_id.currency_id.id
+
if not product_ids:
product_ids = product_obj.search(cr, uid, [])
continue
product = products_by_id[product_id]
quantity_total += qty[product_id]
-
+
# Compute based on pricetype
# Choose the right filed standard_price to read
- amount_unit=product.price_get(pricetype.field, context)[product.id]
+ amount_unit=product.price_get(pricetype.field, context)[product.id]
price = qty[product_id] * amount_unit
# price = qty[product_id] * product.standard_price
class stock_tracking(osv.osv):
_name = "stock.tracking"
_description = "Stock Tracking Lots"
-
+
+ def get_create_tracking_lot(self, cr, uid, ids, tracking_lot):
+ tracking_lot_list = self.search(cr, uid, [('name', '=', tracking_lot)],
+ limit=1)
+ if tracking_lot_list:
+ tracking_lot = tracking_lot_list[0]
+ tracking_obj = self.browse(cr, uid, tracking_lot)
+ if not tracking_obj:
+ tracking_lot_vals = {
+ 'name': tracking_lot
+ }
+ tracking_lot = self.create(cr, uid, tracking_lot_vals)
+ return tracking_lot
def checksum(sscc):
salt = '31' * 8 + '3'
sum = 0
\n* The \'Available\' state is set automatically when the products are ready to be moved.\
\n* The \'Waiting\' state is used in MTO moves when a movement is waiting for another one.'),
'min_date': fields.function(get_min_max_date, fnct_inv=_set_minimum_date, multi="min_max_date",
- method=True, store=True, type='datetime', string='Planned Date', select=1, help="Planned date for Picking. Default it takes current date"),
+ method=True, store=True, type='datetime', string='Expected Date', select=1, help="Expected date for Picking. Default it takes current date"),
'date': fields.datetime('Order Date', help="Date of Order"),
- 'date_done': fields.datetime('Date Done', help="Date of completion"),
+ 'date_done': fields.datetime('Date Done', help="Date of Completion"),
'max_date': fields.function(get_min_max_date, fnct_inv=_set_maximum_date, multi="min_max_date",
- method=True, store=True, type='datetime', string='Max. Planned Date', select=2),
- 'move_lines': fields.one2many('stock.move', 'picking_id', 'Entry lines', states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}),
+ method=True, store=True, type='datetime', string='Max. Expected Date', select=2),
+ 'move_lines': fields.one2many('stock.move', 'picking_id', 'Internal Moves', states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}),
+ 'delivery_line':fields.one2many('stock.delivery', 'picking_id', 'Delivery lines', readonly=True),
'auto_picking': fields.boolean('Auto-Picking'),
'address_id': fields.many2one('res.partner.address', 'Partner', help="Address of partner"),
'invoice_state': fields.selection([
# TODO: Check locations to see if in the same location ?
return True
- def button_confirm(self, cr, uid, ids, *args):
- for id in ids:
- wf_service = netsvc.LocalService("workflow")
- wf_service.trg_validate(uid, 'stock.picking', id, 'button_confirm', cr)
- self.force_assign(cr, uid, ids, *args)
- return True
+# def button_confirm(self, cr, uid, ids, *args):
+# for id in ids:
+# wf_service = netsvc.LocalService("workflow")
+# wf_service.trg_validate(uid, 'stock.picking', id, 'button_confirm', cr)
+# self.force_assign(cr, uid, ids, *args)
+# return True
def action_assign(self, cr, uid, ids, *args):
for pick in self.browse(cr, uid, ids):
move_ids = [x.id for x in pick.move_lines if x.state == 'confirmed']
+ if not move_ids:
+ raise osv.except_osv(_('Warning !'),_('Not Available. Moves are not confirmed.'))
self.pool.get('stock.move').action_assign(cr, uid, move_ids)
return True
context=context)
return True
- def get_currency_id(self, cursor, user, picking):
+ def get_currency_id(self, cr, uid, picking):
return False
- def _get_payment_term(self, cursor, user, picking):
+ def _get_payment_term(self, cr, uid, picking):
'''Return {'contact': address, 'invoice': address} for invoice'''
partner_obj = self.pool.get('res.partner')
partner = picking.address_id.partner_id
return partner.property_payment_term and partner.property_payment_term.id or False
- def _get_address_invoice(self, cursor, user, picking):
+ def _get_address_invoice(self, cr, uid, picking):
'''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(cursor, user, [partner.id],
+ return partner_obj.address_get(cr, uid, [partner.id],
['contact', 'invoice'])
- def _get_comment_invoice(self, cursor, user, picking):
+ def _get_comment_invoice(self, cr, uid, picking):
'''Return comment string for invoice'''
return picking.note or ''
- def _get_price_unit_invoice(self, cursor, user, move_line, type):
+ def _get_price_unit_invoice(self, cr, uid, move_line, type, context=None):
'''Return the price unit for the move line'''
+ if context is None:
+ context = {}
+
if type in ('in_invoice', 'in_refund'):
# Take the user company and pricetype
- price_type_id=self.pool.get('res.users').browse(cr,users,users).company_id.property_valuation_price_type.id
- pricetype=self.pool.get('product.price.type').browse(cr,uid,price_type_id)
- amount_unit=move_line.product_id.price_get(pricetype.field, context)[move_line.product_id.id]
+ price_type_id = self.pool.get('res.users').browse(cr, uid, uid).company_id.property_valuation_price_type.id
+ pricetype = self.pool.get('product.price.type').browse(cr, uid, price_type_id)
+ context['currency_id'] = move_line.company_id.currency_id.id
+
+ amount_unit = move_line.product_id.price_get(pricetype.field, context)[move_line.product_id.id]
return amount_unit
else:
return move_line.product_id.list_price
- def _get_discount_invoice(self, cursor, user, move_line):
+ def _get_discount_invoice(self, cr, uid, move_line):
'''Return the discount for the move line'''
return 0.0
- def _get_taxes_invoice(self, cursor, user, move_line, type):
+ def _get_taxes_invoice(self, cr, uid, move_line, type):
'''Return taxes ids for the move line'''
if type in ('in_invoice', 'in_refund'):
taxes = move_line.product_id.supplier_taxes_id
if move_line.picking_id and move_line.picking_id.address_id and move_line.picking_id.address_id.partner_id:
return self.pool.get('account.fiscal.position').map_tax(
- cursor,
- user,
+ cr,
+ uid,
move_line.picking_id.address_id.partner_id.property_account_position,
taxes
)
else:
return map(lambda x: x.id, taxes)
- def _get_account_analytic_invoice(self, cursor, user, picking, move_line):
+ def _get_account_analytic_invoice(self, cr, uid, picking, move_line):
return False
- def _invoice_line_hook(self, cursor, user, move_line, invoice_line_id):
+ def _invoice_line_hook(self, cr, uid, move_line, invoice_line_id):
'''Call after the creation of the invoice line'''
return
- def _invoice_hook(self, cursor, user, picking, invoice_id):
+ def _invoice_hook(self, cr, uid, picking, invoice_id):
'''Call after the creation of the invoice'''
return
- def action_invoice_create(self, cursor, user, ids, journal_id=False,
+ def action_invoice_create(self, cr, uid, ids, journal_id=False,
group=False, type='out_invoice', context=None):
'''Return ids of created invoices for the pickings'''
+ if context is None:
+ context = {}
+
invoice_obj = self.pool.get('account.invoice')
invoice_line_obj = self.pool.get('account.invoice.line')
invoices_group = {}
res = {}
- for picking in self.browse(cursor, user, ids, context=context):
+ for picking in self.browse(cr, uid, ids, context=context):
if picking.invoice_state != '2binvoiced':
continue
payment_term_id = False
if type in ('out_invoice', 'out_refund'):
account_id = partner.property_account_receivable.id
- payment_term_id = self._get_payment_term(cursor, user, picking)
+ payment_term_id = self._get_payment_term(cr, uid, picking)
else:
account_id = partner.property_account_payable.id
address_contact_id, address_invoice_id = \
- self._get_address_invoice(cursor, user, picking).values()
+ self._get_address_invoice(cr, uid, picking).values()
- comment = self._get_comment_invoice(cursor, user, picking)
+ 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(cursor, user, invoice_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)
}
- invoice_obj.write(cursor, user, [invoice_id], invoice_vals, context=context)
+ invoice_obj.write(cr, uid, [invoice_id], invoice_vals, context=context)
else:
invoice_vals = {
'name': picking.name,
'date_invoice': context.get('date_inv',False),
'company_id': picking.company_id.id,
}
- cur_id = self.get_currency_id(cursor, user, picking)
+ 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(cursor, user, invoice_vals,
+ invoice_id = invoice_obj.create(cr, uid, invoice_vals,
context=context)
invoices_group[partner.id] = invoice_id
res[picking.id] = invoice_id
account_id = move_line.product_id.categ_id.\
property_account_expense_categ.id
- price_unit = self._get_price_unit_invoice(cursor, user,
+ price_unit = self._get_price_unit_invoice(cr, uid,
move_line, type)
- discount = self._get_discount_invoice(cursor, user, move_line)
- tax_ids = self._get_taxes_invoice(cursor, user, move_line, type)
- account_analytic_id = self._get_account_analytic_invoice(cursor,
- user, picking, move_line)
+ discount = self._get_discount_invoice(cr, uid, move_line)
+ tax_ids = self._get_taxes_invoice(cr, uid, move_line, 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 type in ('out_invoice', 'out_refund'):
uos_id = move_line.product_uom.id
- account_id = self.pool.get('account.fiscal.position').map_account(cursor, user, partner.property_account_position, account_id)
+ account_id = self.pool.get('account.fiscal.position').map_account(cr, uid, partner.property_account_position, account_id)
notes = False
if move_line.sale_line_id:
notes = move_line.sale_line_id.notes
elif move_line.purchase_line_id:
notes = move_line.purchase_line_id.notes
- invoice_line_id = invoice_line_obj.create(cursor, user, {
+ invoice_line_id = invoice_line_obj.create(cr, uid, {
'name': name,
'origin': origin,
'invoice_id': invoice_id,
'account_analytic_id': account_analytic_id,
'note': notes,
}, context=context)
- self._invoice_line_hook(cursor, user, move_line, invoice_line_id)
+ self._invoice_line_hook(cr, uid, move_line, invoice_line_id)
- invoice_obj.button_compute(cursor, user, [invoice_id], context=context,
+ invoice_obj.button_compute(cr, uid, [invoice_id], context=context,
set_total=(type in ('in_invoice', 'in_refund')))
- self.write(cursor, user, [picking.id], {
+ self.write(cr, uid, [picking.id], {
'invoice_state': 'invoiced',
}, context=context)
- self._invoice_hook(cursor, user, picking, invoice_id)
- self.write(cursor, user, res.keys(), {
+ self._invoice_hook(cr, uid, picking, invoice_id)
+ self.write(cr, uid, res.keys(), {
'invoice_state': 'invoiced',
}, context=context)
return res
return True
def unlink(self, cr, uid, ids, context=None):
+ move_obj = self.pool.get('stock.move')
+ if not context:
+ context = {}
+
for pick in self.browse(cr, uid, ids, context=context):
if pick.state in ['done','cancel']:
raise osv.except_osv(_('Error'), _('You cannot remove the picking which is in %s state !')%(pick.state,))
- elif pick.state in ['confirmed','assigned']:
+ elif pick.state in ['confirmed','assigned', 'draft']:
ids2 = [move.id for move in pick.move_lines]
- context.update({'call_unlink':True})
- self.pool.get('stock.move').action_cancel(cr, uid, ids2, context)
- else:
- continue
+ ctx = context.copy()
+ ctx.update({'call_unlink':True})
+ if pick.state != 'draft':
+ #Cancelling the move in order to affect Virtual stock of product
+ move_obj.action_cancel(cr, uid, ids2, ctx)
+ #Removing the move
+ move_obj.unlink(cr, uid, ids2, ctx)
+
return super(stock_picking, self).unlink(cr, uid, ids, context=context)
+ def do_partial(self, cr, uid, ids, partial_datas, context={}):
+ """
+ @ partial_datas : dict. contain details of partial picking
+ like partner_id, address_id, delivery_date, delivery moves with product_id, product_qty, uom
+ """
+ res = {}
+ move_obj = self.pool.get('stock.move')
+ delivery_obj = self.pool.get('stock.delivery')
+ product_obj = self.pool.get('product.product')
+ currency_obj = self.pool.get('res.currency')
+ users_obj = self.pool.get('res.users')
+ uom_obj = self.pool.get('product.uom')
+ price_type_obj = self.pool.get('product.price.type')
+ sequence_obj = self.pool.get('ir.sequence')
+ wf_service = netsvc.LocalService("workflow")
+ partner_id = partial_datas.get('partner_id', False)
+ address_id = partial_datas.get('address_id', False)
+ delivery_date = partial_datas.get('delivery_date', False)
+ for pick in self.browse(cr, uid, ids, context=context):
+ new_picking = None
+ new_moves = []
+
+ complete, too_many, too_few = [], [], []
+ move_product_qty = {}
+ for move in pick.move_lines:
+ if move.state in ('done', 'cancel'):
+ continue
+ partial_data = partial_datas.get('move%s'%(move.id), False)
+ assert partial_data, _('Do not Found Partial data of Stock Move Line :%s' %(move.id))
+ product_qty = partial_data.get('product_qty',0.0)
+ move_product_qty[move.id] = product_qty
+ product_uom = partial_data.get('product_uom',False)
+ product_price = partial_data.get('product_price',0.0)
+ product_currency = partial_data.get('product_currency',False)
+ if move.product_qty == product_qty:
+ complete.append(move)
+ elif move.product_qty > product_qty:
+ too_few.append(move)
+ else:
+ too_many.append(move)
+
+ # Average price computation
+ if (pick.type == 'in') and (move.product_id.cost_method == 'average'):
+ product = product_obj.browse(cr, uid, move.product_id.id)
+ user = users_obj.browse(cr, uid, uid)
+ context['currency_id'] = move.company_id.currency_id.id
+ qty = uom_obj._compute_qty(cr, uid, product_uom, product_qty, product.uom_id.id)
+ pricetype = False
+ if user.company_id.property_valuation_price_type:
+ pricetype = price_type_obj.browse(cr, uid, user.company_id.property_valuation_price_type.id)
+ if pricetype and qty > 0:
+ new_price = currency_obj.compute(cr, uid, product_currency,
+ user.company_id.currency_id.id, product_price)
+ new_price = uom_obj._compute_price(cr, uid, product_uom, new_price,
+ product.uom_id.id)
+ if product.qty_available <= 0:
+ new_std_price = new_price
+ else:
+ # Get the standard price
+ amount_unit = product.price_get(pricetype.field, context)[product.id]
+ new_std_price = ((amount_unit * product.qty_available)\
+ + (new_price * qty))/(product.qty_available + qty)
+
+ # Write the field according to price type field
+ product_obj.write(cr, uid, [product.id],
+ {pricetype.field: new_std_price})
+ move_obj.write(cr, uid, [move.id], {'price_unit': new_price})
+
+
+ for move in too_few:
+ product_qty = move_product_qty[move.id]
+ if not new_picking:
+
+ new_picking = self.copy(cr, uid, pick.id,
+ {
+ 'name': sequence_obj.get(cr, uid, 'stock.picking.%s'%(pick.type)),
+ 'move_lines' : [],
+ 'state':'draft',
+ })
+ if product_qty != 0:
+
+ new_obj = move_obj.copy(cr, uid, move.id,
+ {
+ 'product_qty' : product_qty,
+ 'product_uos_qty': product_qty, #TODO: put correct uos_qty
+ 'picking_id' : new_picking,
+ 'state': 'assigned',
+ 'move_dest_id': False,
+ 'price_unit': move.price_unit,
+ })
+
+ 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
+
+ })
+
+ if new_picking:
+ move_obj.write(cr, uid, [c.id for c in complete], {'picking_id': new_picking})
+ for move in too_many:
+ product_qty = move_product_qty[move.id]
+ move_obj.write(cr, uid, [move.id],
+ {
+ 'product_qty' : product_qty,
+ 'product_uos_qty': product_qty, #TODO: put correct uos_qty
+ 'picking_id': new_picking,
+ })
+ else:
+ for move in too_many:
+ product_qty = move_product_qty[move.id]
+ move_obj.write(cr, uid, [move.id],
+ {
+ 'product_qty': product_qty,
+ 'product_uos_qty': product_qty #TODO: put correct uos_qty
+ })
+
+ # At first we confirm the new picking (if necessary)
+ if new_picking:
+ wf_service.trg_validate(uid, 'stock.picking', new_picking, 'button_confirm', cr)
+ # Then we finish the good picking
+ if new_picking:
+ self.write(cr, uid, [pick.id], {'backorder_id': new_picking})
+ self.action_move(cr, uid, [new_picking])
+ wf_service.trg_validate(uid, 'stock.picking', new_picking, 'button_done', cr)
+ wf_service.trg_write(uid, 'stock.picking', pick.id, cr)
+ delivered_pack_id = new_picking
+ else:
+ self.action_move(cr, uid, [pick.id])
+ wf_service.trg_validate(uid, 'stock.picking', pick.id, 'button_done', cr)
+ delivered_pack_id = pick.id
+
+ delivered_pack = self.browse(cr, uid, delivered_pack_id, context=context)
+ delivery_id = delivery_obj.create(cr, uid, {
+ 'name': delivered_pack.name or move.name,
+ 'partner_id': partner_id,
+ 'address_id': address_id,
+ 'date': delivery_date,
+ 'picking_id' : pick.id,
+ 'move_delivered' : [(6,0, map(lambda x:x.id, delivered_pack.move_lines))]
+ }, context=context)
+ res[pick.id] = {'delivered_picking': delivered_pack.id or False}
+ return res
+
stock_picking()
stock_production_lot_revision()
+class stock_delivery(osv.osv):
+
+ """ Tracability of partialdeliveries """
+
+ _name = "stock.delivery"
+ _description = "Delivery"
+ _columns = {
+ 'name': fields.char('Name', size=60, required=True),
+ 'date': fields.datetime('Date', required=True),
+ 'partner_id': fields.many2one('res.partner', 'Partner', required=True),
+ 'address_id': fields.many2one('res.partner.address', 'Address', required=True),
+ 'move_delivered':fields.one2many('stock.move', 'delivered_id', 'Move Delivered'),
+ 'picking_id': fields.many2one('stock.picking', 'Picking list'),
+
+ }
+stock_delivery()
# ----------------------------------------------------
# Move
# ----------------------------------------------------
'date': fields.datetime('Created Date'),
'date_planned': fields.datetime('Date', required=True, help="Scheduled date for the movement of the products or real date if the move is done."),
-
+ 'date_expected': fields.datetime('Date Expected', readonly=True,required=True, help="Scheduled date for the movement of the products"),
'product_id': fields.many2one('product.product', 'Product', required=True, select=True),
'product_qty': fields.float('Quantity', required=True),
help='When the stock move is created it is in the \'Draft\' state.\n After that it is set to \'Confirmed\' state.\n If stock is available state is set to \'Avaiable\'.\n When the picking it done the state is \'Done\'.\
\nThe state is \'Waiting\' if the move is waiting for another one.'),
'price_unit': fields.float('Unit Price',
- digits=(16, int(config['price_accuracy']))),
+ digits_compute= dp.get_precision('Account')),
'company_id': fields.many2one('res.company', 'Company', required=True,select=1),
'partner_id': fields.related('picking_id','address_id','partner_id',type='many2one', relation="res.partner", string="Partner"),
'backorder_id': fields.related('picking_id','backorder_id',type='many2one', relation="stock.picking", string="Back Orders"),
'origin': fields.related('picking_id','origin',type='char', size=64, relation="stock.picking", string="Origin"),
'move_stock_return_history': fields.many2many('stock.move', 'stock_move_return_history', 'move_id', 'return_move_id', 'Move Return History',readonly=True),
+ 'delivered_id': fields.many2one('stock.delivery', 'Product delivered'),
+ 'scraped': fields.related('location_dest_id','scrap_location',type='boolean',relation='stock.location',string='Scraped'),
}
_constraints = [
(_check_tracking,
'state': lambda *a: 'draft',
'priority': lambda *a: '1',
'product_qty': lambda *a: 1.0,
+ 'scraped' : lambda *a: False,
'date_planned': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
'date': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
- 'company_id': lambda self,cr,uid,c: self.pool.get('res.company')._company_default_get(cr, uid, 'stock.move', context=c)
+ 'company_id': lambda self,cr,uid,c: self.pool.get('res.company')._company_default_get(cr, uid, 'stock.move', context=c),
+ 'date_expected': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
}
def copy(self, cr, uid, id, default=None, context={}):
product = self.pool.get('product.product').browse(cr, uid, [prod_id], context=ctx)[0]
uos_id = product.uos_id and product.uos_id.id or False
result = {
- 'name': product.partner_ref,
'product_uom': product.uom_id.id,
'product_uos': uos_id,
'product_qty': 1.00,
'product_uos_qty' : self.pool.get('stock.move').onchange_quantity(cr, uid, ids, prod_id, 1.00, product.uom_id.id, uos_id)['value']['product_uos_qty']
}
-
+ if not ids:
+ result['name'] = product.partner_ref
if loc_id:
result['location_id'] = loc_id
if loc_dest_id:
if dest:
if dest[1] == 'transparent':
self.write(cr, uid, [m.id], {
- 'date_planned': (DateTime.strptime(m.date_planned, '%Y-%m-%d %H:%M:%S') + \
- DateTime.RelativeDateTime(days=dest[2] or 0)).strftime('%Y-%m-%d'),
+ 'date_planned': (datetime.strptime(m.date_planned, '%Y-%m-%d %H:%M:%S') + \
+ relativedelta(days=dest[2] or 0)).strftime('%Y-%m-%d'),
'location_dest_id': dest[0].id})
else:
result.setdefault(m.picking_id, [])
new_moves = []
for picking, todo in self._chain_compute(cr, uid, moves, context).items():
ptype = self.pool.get('stock.location').picking_type_get(cr, uid, todo[0][0].location_dest_id, todo[0][1][0])
+ pick_name = ''
+ if ptype == 'delivery':
+ pick_name = self.pool.get('ir.sequence').get(cr, uid, 'stock.picking.delivery')
pickid = self.pool.get('stock.picking').create(cr, uid, {
- 'name': picking.name,
+ 'name': pick_name or picking.name,
'origin': str(picking.origin or ''),
'type': ptype,
'note': picking.note,
'picking_id': pickid,
'state': 'waiting',
'move_history_ids': [],
- 'date_planned': (DateTime.strptime(move.date_planned, '%Y-%m-%d %H:%M:%S') + DateTime.RelativeDateTime(days=delay or 0)).strftime('%Y-%m-%d'),
+ 'date_planned': (datetime.strptime(move.date_planned, '%Y-%m-%d %H:%M:%S') + relativedelta(days=delay or 0)).strftime('%Y-%m-%d'),
'move_history_ids2': []}
)
self.pool.get('stock.move').write(cr, uid, [move.id], {
#self.action_cancel(cr,uid, ids2, context)
return True
- def action_done(self, cr, uid, ids, context=None):
+ def action_done(self, cr, uid, ids, context={}):
track_flag = False
picking_ids = []
+ product_uom_obj = self.pool.get('product.uom')
+ price_type_obj = self.pool.get('product.price.type')
+ move_obj = self.pool.get('account.move')
for move in self.browse(cr, uid, ids):
if move.picking_id: picking_ids.append(move.picking_id.id)
if move.move_dest_id.id and (move.state != 'done'):
journal_id = move.product_id.categ_id.property_stock_journal.id
if acc_src != acc_dest:
ref = move.picking_id and move.picking_id.name or False
- product_uom_obj = self.pool.get('product.uom')
default_uom = move.product_id.uom_id.id
date = time.strftime('%Y-%m-%d')
q = product_uom_obj._compute_qty(cr, uid, move.product_uom.id, move.product_qty, default_uom)
amount = q * move.price_unit
# Base computation on valuation price type
else:
- company_id=move.company_id.id
-
- pricetype=self.pool.get('product.price.type').browse(cr,uid,move.company_id.property_valuation_price_type.id)
- amount_unit=move.product_id.price_get(pricetype.field, context)[move.product_id.id]
- amount=amount_unit * q or 1.0
+ company_id = move.company_id.id
+ context['currency_id'] = move.company_id.currency_id.id
+ pricetype = price_type_obj.browse(cr,uid,move.company_id.property_valuation_price_type.id)
+ amount_unit = move.product_id.price_get(pricetype.field, context)[move.product_id.id]
+ amount = amount_unit * q or 1.0
# amount = q * move.product_id.standard_price
-
+
partner_id = False
if move.picking_id:
partner_id = move.picking_id.address_id and (move.picking_id.address_id.partner_id and move.picking_id.address_id.partner_id.id or False) or False
'date': date,
'partner_id': partner_id})
]
- self.pool.get('account.move').create(cr, uid, {
+ move_obj.create(cr, uid, {
'name': move.name,
'journal_id': journal_id,
'line_id': lines,
'ref': ref,
})
- self.write(cr, uid, ids, {'state': 'done', 'date_planned': time.strftime('%Y-%m-%d %H:%M:%S')})
- for pick in self.pool.get('stock.picking').browse(cr, uid, picking_ids):
+ tracking_lot = False
+ if context:
+ tracking_lot = context.get('tracking_lot', False)
+ if tracking_lot:
+ rec_id = context and context.get('active_id', False)
+ tracking = self.pool.get('stock.tracking')
+ tracking_lot = tracking.get_create_tracking_lot(cr, uid,[rec_id], tracking_lot)
+
+ self.write(cr, uid, ids, {'state': 'done', 'date_planned': time.strftime('%Y-%m-%d %H:%M:%S'), 'tracking_id': tracking_lot or False})
+ picking_obj = self.pool.get('stock.picking')
+ for pick in picking_obj.browse(cr, uid, picking_ids):
if all(move.state == 'done' for move in pick.move_lines):
- self.pool.get('stock.picking').action_done(cr, uid, [pick.id])
+ picking_obj.action_done(cr, uid, [pick.id])
wf_service = netsvc.LocalService("workflow")
for id in ids:
return True
def unlink(self, cr, uid, ids, context=None):
+ if context is None:
+ context = {}
for move in self.browse(cr, uid, ids, context=context):
if move.state != 'draft':
raise osv.except_osv(_('UserError'),
_('You can only delete draft moves.'))
return super(stock_move, self).unlink(
- cr, uid, ids, context=context)
+ cr, uid, ids, context=context)
- def split_lines(self, cr, uid, ids, quantity, split_by_qty=1, prefix=False, context=None):
+ def _create_lot(self, cr, uid, ids, product_id, prefix=False):
prodlot_obj = self.pool.get('stock.production.lot')
ir_sequence_obj = self.pool.get('ir.sequence')
-
sequence = ir_sequence_obj.get(cr, uid, 'stock.lot.serial')
if not sequence:
- raise wizard.except_wizard(_('Error!'), _('No production sequence defined'))
- new_move = []
-
- def _create_lot(move_id, product_id):
- prodlot_id = prodlot_obj.create(cr, uid, {'name': sequence, 'prefix': prefix}, {'product_id': product_id})
- prodlot = prodlot_obj.browse(cr, uid, prodlot_id)
- ref = '%d' % (move_id)
- if prodlot.ref:
- ref = '%s, %s' % (prodlot.ref, ref)
- prodlot_obj.write(cr, uid, [prodlot_id], {'ref': ref})
- return prodlot_id
-
- for move in self.browse(cr, uid, ids):
+ raise osv.except_osv(_('Error!'), _('No production sequence defined'))
+ prodlot_id = prodlot_obj.create(cr, uid, {'name': sequence, 'prefix': prefix}, {'product_id': product_id})
+ prodlot = prodlot_obj.browse(cr, uid, prodlot_id)
+ ref = ','.join(map(lambda x:str(x),ids))
+ if prodlot.ref:
+ ref = '%s, %s' % (prodlot.ref, ref)
+ prodlot_obj.write(cr, uid, [prodlot_id], {'ref': ref})
+ return prodlot_id
+
+
+ def action_scrap(self, cr, uid, ids, quantity, location_id, context=None):
+ '''
+ Move the scrap/damaged product into scrap location
+
+ @ param cr: the database cursor
+ @ param uid: the user id
+ @ param ids: ids of stock move object to be scraped
+ @ param quantity : specify scrap qty
+ @ param location_id : specify scrap location
+ @ param context: context arguments
+
+ @ return: Scraped lines
+ '''
+ if quantity <= 0:
+ raise osv.except_osv(_('Warning!'), _('Please provide Proper Quantity !'))
+ res = []
+ for move in self.browse(cr, uid, ids, context=context):
+ move_qty = move.product_qty
+ uos_qty = quantity / move_qty * move.product_uos_qty
+ default_val = {
+ 'product_qty': quantity,
+ 'product_uos_qty': uos_qty,
+ 'state': move.state,
+ 'scraped' : True,
+ 'location_dest_id': location_id
+ }
+ new_move = self.copy(cr, uid, move.id, default_val)
+ #self.write(cr, uid, [new_move], {'move_history_ids':[(4,move.id)]}) #TODO : to track scrap moves
+ res += [new_move]
+ self.action_done(cr, uid, res)
+ return res
+
+ def action_split(self, cr, uid, ids, quantity, split_by_qty=1, prefix=False, with_lot=True, context=None):
+ '''
+ Split Stock Move lines into production lot which specified split by quantity.
+
+ @ param cr: the database cursor
+ @ param uid: the user id
+ @ param ids: ids of stock move object to be splited
+ @ param split_by_qty : specify split by qty
+ @ param prefix : specify prefix of production lot
+ @ param with_lot : if true, prodcution lot will assign for split line otherwise not.
+ @ param context: context arguments
+
+ @ return: splited move lines
+ '''
+
+ if quantity <= 0:
+ raise osv.except_osv(_('Warning!'), _('Please provide Proper Quantity !'))
+
+ res = []
+
+ for move in self.browse(cr, uid, ids):
if split_by_qty <= 0 or quantity == 0:
return res
update_val = {
'product_qty': split_by_qty,
'product_uos_qty': uos_qty,
- }
+ }
for idx in range(int(quantity//split_by_qty)):
- if idx:
- current_move = self.copy(cr, uid, move.id, {'state': move.state})
- new_move.append(current_move)
- else:
+ if not idx and move.product_qty<=quantity:
current_move = move.id
-
+ else:
+ current_move = self.copy(cr, uid, move.id, {'state': move.state})
+ res.append(current_move)
+ if with_lot:
+ update_val['prodlot_id'] = self._create_lot(cr, uid, [current_move], move.product_id.id)
- update_val['prodlot_id'] = _create_lot(current_move, move.product_id.id)
self.write(cr, uid, [current_move], update_val)
-
-
+
+
if quantity_rest > 0:
idx = int(quantity//split_by_qty)
update_val['product_qty'] = quantity_rest
update_val['product_uos_qty'] = uos_qty_rest
- if idx:
- current_move = self.copy(cr, uid, move.id, {'state': move.state})
- new_move.append(current_move)
+ if not idx and move.product_qty<=quantity:
+ current_move = move.id
else:
- current_move = move.id
- update_val['prodlot_id'] = _create_lot(current_move, move.product_id.id)
+ current_move = self.copy(cr, uid, move.id, {'state': move.state})
+
+ res.append(current_move)
+
+
+ if with_lot:
+ update_val['prodlot_id'] = self._create_lot(cr, uid, [current_move], move.product_id.id)
+
self.write(cr, uid, [current_move], update_val)
- return new_move
+ return res
+
+ def action_consume(self, cr, uid, ids, quantity, location_id=False, context=None):
+ '''
+ Consumed product with specific quatity from specific source location
+
+ @ param cr: the database cursor
+ @ param uid: the user id
+ @ param ids: ids of stock move object to be consumed
+ @ param quantity : specify consume quantity
+ @ param location_id : specify source location
+ @ param context: context arguments
+
+ @ return: Consumed lines
+ '''
+ if context is None:
+ context = {}
- def consume_moves(self, cr, uid, ids, quantity, location_id, context=None):
- new_move = []
- if not context:
- context = {}
if quantity <= 0:
raise osv.except_osv(_('Warning!'), _('Please provide Proper Quantity !'))
-
+
+ res = []
for move in self.browse(cr, uid, ids, context=context):
move_qty = move.product_qty
quantity_rest = move.product_qty
quantity_rest -= quantity
- uos_qty = quantity / move_qty * move.product_uos_qty
uos_qty_rest = quantity_rest / move_qty * move.product_uos_qty
if quantity_rest <= 0:
- quantity_rest = quantity
- break
+ quantity_rest = 0
+ uos_qty_rest = 0
+ quantity = move.product_qty
- default_val = {
- 'product_qty': quantity,
- 'product_uos_qty': uos_qty,
- 'location_id' : location_id,
- }
- if move.product_id.track_production:
- new_move = self.split_lines(cr, uid, [move.id], quantity, split_by_qty=1, context=context)
- else:
- current_move = self.copy(cr, uid, move.id, default_val)
- new_move.append(current_move)
+ uos_qty = quantity / move_qty * move.product_uos_qty
+
+ if quantity_rest > 0:
+ default_val = {
+ 'product_qty': quantity,
+ 'product_uos_qty': uos_qty,
+ 'state': move.state,
+ 'location_id': location_id
+ }
+ if move.product_id.track_production and location_id:
+ # IF product has checked track for production lot, move lines will be split by 1
+ res += self.action_split(cr, uid, [move.id], quantity, split_by_qty=1, context=context)
+ else:
+ current_move = self.copy(cr, uid, move.id, default_val)
+ res += [current_move]
- update_val = {}
- if quantity_rest > 0:
+ update_val = {}
update_val['product_qty'] = quantity_rest
- update_val['product_uos_qty'] = uos_qty_rest
+ update_val['product_uos_qty'] = uos_qty_rest
self.write(cr, uid, [move.id], update_val)
-
- self.action_done(cr, uid, new_move)
- return new_move
- def scrap_moves(self, cr, uid, ids, quantity, location_dest_id, context=None):
- new_move = []
- if quantity <= 0:
- raise osv.except_osv(_('Warning!'), _('Please provide Proper Quantity !'))
+ else:
+ quantity_rest = quantity
+ uos_qty_rest = uos_qty
+
+ if move.product_id.track_production and location_id:
+ res += self.split_lines(cr, uid, [move.id], quantity_rest, split_by_qty=1, context=context)
+ else:
+ res += [move.id]
+ update_val = {
+ 'product_qty' : quantity_rest,
+ 'product_uos_qty' : uos_qty_rest,
+ 'location_id': location_id
+ }
+
+ self.write(cr, uid, [move.id], update_val)
+
+ self.action_done(cr, uid, res)
+ return res
+
+ def do_partial(self, cr, uid, ids, partial_datas, context={}):
+ """
+ @ partial_datas : dict. contain details of partial picking
+ like partner_id, address_id, delivery_date, delivery moves with product_id, product_qty, uom
+ """
+ res = {}
+ picking_obj = self.pool.get('stock.picking')
+ delivery_obj = self.pool.get('stock.delivery')
+ product_obj = self.pool.get('product.product')
+ currency_obj = self.pool.get('res.currency')
+ users_obj = self.pool.get('res.users')
+ uom_obj = self.pool.get('product.uom')
+ price_type_obj = self.pool.get('product.price.type')
+ sequence_obj = self.pool.get('ir.sequence')
+ wf_service = netsvc.LocalService("workflow")
+ partner_id = partial_datas.get('partner_id', False)
+ address_id = partial_datas.get('address_id', False)
+ delivery_date = partial_datas.get('delivery_date', False)
+ tracking_lot = context.get('tracking_lot', False)
+ new_moves = []
+
+ complete, too_many, too_few = [], [], []
+ move_product_qty = {}
for move in self.browse(cr, uid, ids, context=context):
- location = move.location_dest_id.id
- move_qty = move.product_qty
- quantity_rest = move.product_qty
+ if move.state in ('done', 'cancel'):
+ continue
+ partial_data = partial_datas.get('move%s'%(move.id), False)
+ assert partial_data, _('Do not Found Partial data of Stock Move Line :%s' %(move.id))
+ product_qty = partial_data.get('product_qty',0.0)
+ move_product_qty[move.id] = product_qty
+ product_uom = partial_data.get('product_uom',False)
+ product_price = partial_data.get('product_price',0.0)
+ product_currency = partial_data.get('product_currency',False)
+ if move.product_qty == product_qty:
+ self.write(cr, uid, move.id,
+ {
+ 'tracking_id': tracking_lot
+ })
+ complete.append(move)
+ elif move.product_qty > product_qty:
+ too_few.append(move)
+ else:
+ too_many.append(move)
+
+ # Average price computation
+ if (move.picking_id.type == 'in') and (move.product_id.cost_method == 'average'):
+ product = product_obj.browse(cr, uid, move.product_id.id)
+ user = users_obj.browse(cr, uid, uid)
+ context['currency_id'] = move.company_id.currency_id.id
+ qty = uom_obj._compute_qty(cr, uid, product_uom, product_qty, product.uom_id.id)
+ pricetype = False
+ if user.company_id.property_valuation_price_type:
+ pricetype = price_type_obj.browse(cr, uid, user.company_id.property_valuation_price_type.id)
+ if pricetype and qty > 0:
+ new_price = currency_obj.compute(cr, uid, product_currency,
+ user.company_id.currency_id.id, product_price)
+ new_price = uom_obj._compute_price(cr, uid, product_uom, new_price,
+ product.uom_id.id)
+ if product.qty_available <= 0:
+ new_std_price = new_price
+ else:
+ # Get the standard price
+ amount_unit = product.price_get(pricetype.field, context)[product.id]
+ new_std_price = ((amount_unit * product.qty_available)\
+ + (new_price * qty))/(product.qty_available + qty)
+
+ # Write the field according to price type field
+ product_obj.write(cr, uid, [product.id],
+ {pricetype.field: new_std_price})
+ self.write(cr, uid, [move.id], {'price_unit': new_price})
+
+ for move in too_few:
+ product_qty = move_product_qty[move.id]
+ if product_qty != 0:
+ new_move = self.copy(cr, uid, move.id,
+ {
+ 'product_qty' : product_qty,
+ 'product_uos_qty': product_qty,
+ 'picking_id' : move.picking_id.id,
+ 'state': 'assigned',
+ 'move_dest_id': False,
+ 'price_unit': move.price_unit,
+ 'tracking_id': tracking_lot,
+ })
+ complete.append(self.browse(cr, uid, new_move))
+ self.write(cr, uid, move.id,
+ {
+ 'product_qty' : move.product_qty - product_qty,
+ 'product_uos_qty':move.product_qty - product_qty,
+ })
- quantity_rest -= quantity
- uos_qty = quantity / move_qty * move.product_uos_qty
- uos_qty_rest = quantity_rest / move_qty * move.product_uos_qty
- if quantity_rest <= 0:
- quantity_rest = quantity
- break
-
- default_val = {
- 'product_qty': quantity,
- 'product_uos_qty': uos_qty,
- 'location_dest_id' : location_dest_id,
- 'state': move.state
- }
- current_move = self.copy(cr, uid, move.id, default_val)
- new_move.append(current_move)
- update_val = {}
- if quantity_rest > 0:
- update_val['product_qty'] = quantity_rest
- update_val['product_uos_qty'] = uos_qty_rest
- self.write(cr, uid, [move.id], update_val)
- return new_move
+
+ for move in too_many:
+ self.write(cr, uid, move.id,
+ {
+ 'product_qty': move.product_qty,
+ 'product_uos_qty': move.product_qty,
+ 'tracking_id': tracking_lot
+ })
+ complete.append(move)
+
+ for move in complete:
+ self.action_done(cr, uid, [move.id], context)
+
+ # TOCHECK : Done picking if all moves are done
+ cr.execute("""
+ SELECT move.id FROM stock_picking pick
+ RIGHT JOIN stock_move move ON move.picking_id = pick.id AND move.state = %s
+ WHERE pick.id = %s""",
+ ('done', move.picking_id.id))
+ res = cr.fetchall()
+ if len(res) == len(move.picking_id.move_lines):
+ picking_obj.action_move(cr, uid, [move.picking_id.id])
+ wf_service.trg_validate(uid, 'stock.picking', move.picking_id.id, 'button_done', cr)
+
+ ref = {}
+ done_move_ids = []
+ for move in complete:
+ done_move_ids.append(move.id)
+ if move.picking_id.id not in ref:
+ delivery_id = delivery_obj.create(cr, uid, {
+ 'partner_id': partner_id,
+ 'address_id': address_id,
+ 'date': delivery_date,
+ 'name' : move.picking_id.name,
+ 'picking_id': move.picking_id.id
+ }, context=context)
+ ref[move.picking_id.id] = delivery_id
+ delivery_obj.write(cr, uid, ref[move.picking_id.id], {
+ 'move_delivered' : [(4,move.id)]
+ })
+ return done_move_ids
stock_move()
'company_id': lambda self,cr,uid,c: self.pool.get('res.company')._company_default_get(cr, uid, 'stock.inventory', context=c)
}
- #
- # Update to support tracking
- #
+
+ def _inventory_line_hook(self, cr, uid, inventory_line, move_vals):
+ '''Creates a stock move from an inventory line'''
+ return self.pool.get('stock.move').create(cr, uid, move_vals)
+
def action_done(self, cr, uid, ids, context=None):
for inv in self.browse(cr, uid, ids):
move_ids = []
move_line = []
for line in inv.inventory_line_id:
pid = line.product_id.id
-
+
# price = line.product_id.standard_price or 0.0
amount = self.pool.get('stock.location')._product_get(cr, uid, line.location_id.id, [pid], {'uom': line.product_uom.id})[pid]
change = line.product_qty - amount
'prodlot_id': lot_id,
'product_qty': line.product_qty
})
- move_ids.append(self.pool.get('stock.move').create(cr, uid, value))
+ move_ids.append(self._inventory_line_hook(cr, uid, line, value))
if len(move_ids):
self.pool.get('stock.move').action_done(cr, uid, move_ids,
context=context)
'product_uom': fields.many2one('product.uom', 'Product UOM', required=True),
'product_qty': fields.float('Quantity'),
'company_id': fields.related('inventory_id','company_id',type='many2one',relation='res.company',string='Company',store=True),
- 'prod_lot_id': fields.many2one('stock.production.lot', 'Production Lot', domain="[('product_id','=',product_id)]")
+ 'prod_lot_id': fields.many2one('stock.production.lot', 'Production Lot', domain="[('product_id','=',product_id)]"),
+ 'state': fields.related('inventory_id','state',type='char',string='State',readonly=True),
}
+
def on_change_product_id(self, cr, uid, ids, location_id, product, uom=False):
if not product:
return {}
# 'partner_id': fields.many2one('res.partner', 'Owner'),
'company_id': fields.many2one('res.company','Company',required=True,select=1),
'partner_address_id': fields.many2one('res.partner.address', 'Owner Address'),
- 'lot_input_id': fields.many2one('stock.location', 'Location Input', required=True),
- 'lot_stock_id': fields.many2one('stock.location', 'Location Stock', required=True),
- 'lot_output_id': fields.many2one('stock.location', 'Location Output', required=True),
+ 'lot_input_id': fields.many2one('stock.location', 'Location Input', required=True, domain=[('usage','<>','view')]),
+ 'lot_stock_id': fields.many2one('stock.location', 'Location Stock', required=True, domain=[('usage','<>','view')]),
+ 'lot_output_id': fields.many2one('stock.location', 'Location Output', required=True, domain=[('usage','<>','view')]),
}
_defaults = {
'company_id': lambda self,cr,uid,c: self.pool.get('res.company')._company_default_get(cr, uid, 'stock.inventory', context=c),
if line.picking_id:
picking_obj.write(cr, uid, [line.picking_id.id], {'move_lines': [(1, line.id, {'picking_id': act['picking_id']})]})
picking_obj.write(cr, uid, [act['picking_id']], {'move_lines': [(1, line.id, {'picking_id': act['picking_id']})]})
- cr.commit()
old_picking = picking_obj.read(cr, uid, [line.picking_id.id])[0]
if not len(old_picking['move_lines']):
picking_obj.write(cr, uid, [old_picking['id']], {'state': 'done'})
tools.drop_view_if_exists(cr, 'report_products_to_received_planned')
cr.execute("""
create or replace view report_products_to_received_planned as (
- select stock.date, min(stock.id), sum(stock.product_qty) as qty, 0 as planned_qty
+ select stock.date, min(stock.id) as id, sum(stock.product_qty) as qty, 0 as planned_qty
from stock_picking picking
inner join stock_move stock
on picking.id = stock.picking_id and picking.type = 'in'
union
- select stock.date_planned, min(stock.id), 0 as actual_qty, sum(stock.product_qty) as planned_qty
+ select stock.date_planned, min(stock.id) as id, 0 as actual_qty, sum(stock.product_qty) as planned_qty
from stock_picking picking
inner join stock_move stock
on picking.id = stock.picking_id and picking.type = 'in'
tools.drop_view_if_exists(cr, 'report_delivery_products_planned')
cr.execute("""
create or replace view report_delivery_products_planned as (
- select stock.date, min(stock.id), sum(stock.product_qty) as qty, 0 as planned_qty
+ select stock.date, min(stock.id) as id, sum(stock.product_qty) as qty, 0 as planned_qty
from stock_picking picking
inner join stock_move stock
on picking.id = stock.picking_id and picking.type = 'out'