Files
tryton/move.py
T

337 lines
15 KiB
Python
Raw Normal View History

2008-06-23 21:01:22 +02:00
#This file is part of Tryton. The COPYRIGHT file at the top level of this repository contains the full copyright notices and license terms.
2008-04-08 14:27:53 +02:00
"Move"
2008-07-17 19:37:38 +02:00
from trytond.osv import fields, OSV
2008-05-13 11:49:37 +02:00
from decimal import Decimal
2008-05-13 13:11:53 +02:00
import datetime
2008-04-08 14:27:53 +02:00
STATES = {
2008-04-17 02:31:55 +02:00
'readonly': "(state in ('cancel', 'done'))",
2008-04-08 14:27:53 +02:00
}
2008-04-08 14:27:53 +02:00
class Move(OSV):
"Stock Move"
_name = 'stock.move'
_description = __doc__
_rec_name = "product"
2008-05-13 11:49:37 +02:00
product = fields.Many2One("product.product", "Product", required=True,
select=1, states=STATES,
on_change=['product', 'type', 'currency', 'uom', 'company'],
2008-06-11 20:18:37 +02:00
domain=[('type', '!=', 'service')])
2008-08-26 19:05:47 +02:00
uom = fields.Many2One("product.uom", "Uom", required=True, states=STATES,
domain="[('category', '=', " \
"(product, 'product.default_uom.category'))]")
2008-08-26 19:05:47 +02:00
unit_digits = fields.Function('get_unit_digits', type='integer',
string='Unit Digits', on_change_with=['uom'])
quantity = fields.Float("Quantity", required=True,
digits="(16, unit_digits)", states=STATES)
2008-05-13 11:49:37 +02:00
from_location = fields.Many2One("stock.location", "From Location", select=1,
required=True, states=STATES,
domain=[('type', '!=', 'warehouse')],
on_change=['from_location', 'to_location'])
to_location = fields.Many2One("stock.location", "To Location", select=1,
required=True, states=STATES,
domain=[('type', '!=', 'warehouse')],
on_change=['from_location', 'to_location'])
packing_in = fields.Many2One('stock.packing.in', 'Supplier Packing',
readonly=True, select=1)
packing_out = fields.Many2One('stock.packing.out', 'Customer Packing',
readonly=True, select=1)
2008-05-13 11:49:37 +02:00
planned_date = fields.Date("Planned Date", states=STATES)
2008-04-17 17:23:37 +02:00
effective_date = fields.Date("Effective Date", readonly=True)
state = fields.Selection([
('draft', 'Draft'),
2008-07-07 15:54:10 +02:00
('assigned', 'Assigned'),
('done', 'Done'),
('cancel', 'Cancel'),
], 'State', select=1, readonly=True)
2008-05-09 12:32:46 +02:00
company = fields.Many2One('company.company', 'Company', required=True,
states={
'readonly': "state != 'draft'",
})
2008-05-13 11:49:37 +02:00
unit_price = fields.Numeric('Unit Price', digits=(16, 4),
states={
'invisible': "type not in ('input', 'output')",
'required': "type in ('input', 'output')",
2008-07-07 15:54:10 +02:00
'readonly': "state not in ('draft',)",
2008-05-13 11:49:37 +02:00
})
currency = fields.Many2One('currency.currency', 'Currency',
states={
'invisible': "type not in ('input', 'output')",
'required': "type in ('input', 'output')",
2008-07-07 15:54:10 +02:00
'readonly': "state not in ('draft',)",
2008-05-13 11:49:37 +02:00
})
type = fields.Function('get_type', type='selection',
selection=[
('input', 'Input'),
('output', 'Output'),
('internal', 'Internal'),
], string='Type')
2008-04-08 14:27:53 +02:00
def __init__(self):
super(Move, self).__init__()
self._rpc_allowed += [
'set_state_done',
'set_state_draft',
'set_state_cancel',
]
2008-04-22 00:50:23 +02:00
self._sql_constraints += [
('check_move_qty_pos',
'CHECK(quantity >= 0.0)', 'Move quantity must be positive'),
('check_from_to_locations',
'CHECK(from_location != to_location)',
'Source and destination location must be different'),
('check_packing_in_out',
'CHECK(NOT(packing_in IS NOT NULL ' \
'AND packing_out IS NOT NULL))',
'Move can not be in both Supplier and Customer Packing'),
2008-04-22 00:50:23 +02:00
]
self._constraints += [
2008-08-05 11:36:37 +02:00
('check_product_type', 'service_product'),
]
2008-04-22 17:49:50 +02:00
self._order[0] = ('id', 'DESC')
2008-07-17 19:37:38 +02:00
self._error_messages.update({
'set_state_draft': 'You can not set state to draft!',
'set_state_assigned': 'You can not set state to assigned!',
'set_state_done': 'You can not set state to done!',
'del_draft_cancel': 'You can only delete draft or cancelled moves!',
2008-08-05 11:36:37 +02:00
'service_product': 'You can not use service product for a move!',
2008-07-17 19:37:38 +02:00
})
2008-04-22 00:50:23 +02:00
2008-04-17 02:31:55 +02:00
def default_to_location(self, cursor, user, context=None):
2008-04-24 00:49:17 +02:00
if context and context.get('warehouse') and context.get('type'):
wh_location = self.pool.get('stock.location').browse(
cursor, user, context['warehouse'], context=context)
field = {'inventory_in': 'storage_location',
'inventory_out': 'output_location',
'incoming': 'input_location',}.get(context['type'])
return field and wh_location[field].id or False
2008-04-17 02:31:55 +02:00
def default_from_location(self, cursor, user, context=None):
2008-04-24 00:49:17 +02:00
if context and context.get('warehouse') and context.get('type'):
wh_location = self.pool.get('stock.location').browse(
cursor, user, context['warehouse'], context=context)
field = {'inventory_in': 'input_location',
'inventory_out': 'storage_location',
'outgoing': 'output_location',}.get(context['type'])
return field and wh_location[field].id or False
2008-04-17 02:31:55 +02:00
2008-04-08 14:27:53 +02:00
def default_state(self, cursor, user, context=None):
return 'draft'
2008-05-09 12:32:46 +02:00
def default_company(self, cursor, user, context=None):
company_obj = self.pool.get('company.company')
if context is None:
context = {}
if context.get('company'):
return company_obj.name_get(cursor, user, context['company'],
context=context)[0]
return False
2008-05-13 11:49:37 +02:00
def default_currency(self, cursor, user, context=None):
company_obj = self.pool.get('company.company')
currency_obj = self.pool.get('currency.currency')
if context is None:
context = {}
company = None
if context.get('company'):
company = company_obj.browse(cursor, user, context['company'],
context=context)
return currency_obj.name_get(cursor, user, company.currency.id,
context=context)[0]
return False
2008-04-08 14:27:53 +02:00
2008-05-13 11:49:37 +02:00
def get_type(self, cursor, user, ids, name, args, context=None):
res = {}
for move in self.browse(cursor, user, ids, context=context):
res[move.id] = 'internal'
if move.from_location.type == 'supplier':
res[move.id] = 'input'
if move.to_location.type == 'customer':
res[move.id] = 'output'
return res
2008-08-26 19:05:47 +02:00
def on_change_with_unit_digits(self, cursor, user, ids, vals,
context=None):
uom_obj = self.pool.get('product.uom')
if vals.get('unit'):
uom = uom_obj.browse(cursor, user, vals['unit'],
context=context)
return uom.digits
return 2
def get_unit_digits(self, cursor, user, ids, name, arg, context=None):
res = {}
for move in self.browse(cursor, user, ids, context=context):
res[move.id] = move.uom.digits
return res
2008-05-13 11:49:37 +02:00
def on_change_product(self, cursor, user, ids, vals, context=None):
product_obj = self.pool.get('product.product')
uom_obj = self.pool.get('product.uom')
currency_obj = self.pool.get('currency.currency')
company_obj = self.pool.get('company.company')
if context is None:
context = {}
res = {'unit_price': Decimal('0.0')}
if vals.get('product'):
product = product_obj.browse(cursor, user, vals['product'],
context=context)
res['uom'] = uom_obj.name_get(cursor, user,
product.default_uom.id, context=context)[0]
2008-08-26 19:05:47 +02:00
res['unit_digits'] = product.default_uom.digits
2008-05-13 11:49:37 +02:00
if vals.get('type', 'internal') == 'input':
unit_price = product.cost_price
if vals.get('uom') and vals['uom'] != product.default_uom.id:
uom = uom_obj.browse(cursor, user, vals['uom'],
context=context)
2008-05-13 11:49:37 +02:00
unit_price = uom_obj.compute_price(cursor, user,
product.default_uom, unit_price, uom,
context=context)
2008-05-13 11:49:37 +02:00
if vals.get('currency') and vals.get('company'):
currency = currency_obj.browse(cursor, user,
vals['currency'], context=context)
company = company_obj.browse(cursor, user,
vals['company'], context=context)
unit_price = currency_obj.compute(cursor, user,
company.currency, unit_price, currency,
context=context)
res['unit_price'] = unit_price
return res
def _on_change_location(self, cursor, user, ids, vals, context=None):
location_obj = self.pool.get('stock.location')
res = {'type': 'internal'}
if vals.get('from_location'):
location = location_obj.browse(cursor, user, vals['from_location'],
context=context)
if location.type == 'supplier':
res['type'] = 'input'
if vals.get('to_location'):
location = location_obj.browse(cursor, user, vals['to_location'],
context=context)
if location.type == 'customer':
res['type'] = 'output'
return res
def check_product_type(self, cursor, user, ids):
for move in self.browse(cursor, user, ids):
if move.product.type == 'service':
return False
return True
2008-05-13 11:49:37 +02:00
def on_change_from_location(self, cursor, user, ids, vals, context=None):
return self._on_change_location(cursor, user, ids, vals,
context=context)
def on_change_to_location(self, cursor, user, ids, vals, context=None):
return self._on_change_location(cursor, user, ids, vals,
context=context)
2008-04-08 14:27:53 +02:00
def set_state_done(self, cursor, user, ids, context=None):
2008-05-13 13:11:53 +02:00
return self.write(cursor, user, ids, {
'state': 'done',
'effective_date': datetime.datetime.now(),
}, context=context)
2008-04-08 14:27:53 +02:00
def set_state_draft(self, cursor, user, ids, context=None):
2008-05-13 13:11:53 +02:00
return self.write(cursor, user, ids, {
'state': 'draft',
}, context=context)
2008-04-08 14:27:53 +02:00
def set_state_cancel(self, cursor, user, ids, context=None):
2008-05-13 13:11:53 +02:00
return self.write(cursor, user, ids, {
'state': 'cancel',
}, context=context)
def set_state_assigned(self, cursor, user, ids, context=None):
2008-05-13 13:11:53 +02:00
return self.write(cursor, user, ids, {
2008-05-13 16:47:26 +02:00
'state': 'assigned',
2008-05-13 13:11:53 +02:00
}, context=context)
def search(self, cursor, user, args, offset=0, limit=None, order=None,
context=None, count=False, query_string=False):
location_obj = self.pool.get('stock.location')
args = args[:]
2008-09-04 18:29:14 +02:00
def process_args(args):
i = 0
while i < len(args):
if isinstance(args[i], list):
process_args(args[i])
if isinstance(args[i], tuple) \
and args[i][0] == 'to_location_warehouse':
location_id = False
if args[i][2]:
location = location_obj.browse(cursor, user,
args[i][2], context=context)
if location.type == 'warehouse':
location_id = location.input_location.id
args[i] = ('to_location', args[i][1], location_id)
i += 1
process_args(args)
return super(Move, self).search(cursor, user, args, offset=offset,
limit=limit, order=order, context=context, count=count,
query_string=query_string)
2008-05-13 13:11:53 +02:00
def write(self, cursor, user, ids, vals, context=None):
uom_obj = self.pool.get('product.uom')
product_obj = self.pool.get('product.product')
location_obj = self.pool.get('stock.location')
if context is None:
context = {}
2008-05-19 17:50:15 +02:00
if isinstance(ids, (int, long)):
ids = [ids]
2008-05-13 13:11:53 +02:00
if 'state' in vals:
for move in self.browse(cursor, user, ids, context=context):
if vals['state'] == 'draft':
if move.state in ('assigned', 'done'):
2008-07-17 19:37:38 +02:00
self.raise_user_error(cursor, 'set_state_draft',
context=context)
2008-05-13 13:11:53 +02:00
elif vals['state'] == 'assigned':
if move.state in ('cancel', 'done'):
2008-07-17 19:37:38 +02:00
self.raise_user_error(cursor, 'set_state_assigned',
context=context)
2008-05-13 13:11:53 +02:00
elif vals['state'] == 'done':
if move.state in ('cancel'):
2008-07-17 19:37:38 +02:00
self.raise_user_error(cursor, 'set_state_done',
context=context)
2008-05-13 13:11:53 +02:00
if move.type == 'input' \
and move.product.cost_price_method == 'average':
ctx = context.copy()
ctx['locations'] = location_obj.search(cursor, user, [
('type', 'in', 'storage'),
], context=context)
product = product_obj.browse(cursor, user,
move.product.id, context=ctx)
qty = uom_obj.compute_qty(cursor, user, move.uom,
move.quantity, product.default_uom,
context=context)
2008-05-13 13:11:53 +02:00
if qty > 0:
qty = Decimal(str(qty))
product_qty = Decimal(str(product.quantity))
unit_price = uom_obj.compute_price(cursor, user,
move.uom, move.unit_price,
product.default_uom, context=context)
2008-05-13 13:11:53 +02:00
new_cost_price = (\
(product.cost_price * product_qty) \
+ (unit_price * qty)) \
/ (product_qty + qty)
product_obj.write(cursor, user, product.id, {
'cost_price': new_cost_price,
}, context=context)
return super(Move, self).write(cursor, user, ids, vals, context=context)
2008-07-25 12:49:38 +02:00
def delete(self, cursor, user, ids, context=None):
2008-07-07 15:54:10 +02:00
for move in self.browse(cursor, user, ids, context=context):
2008-07-11 00:27:42 +02:00
if move.state not in ('draft', 'cancel'):
2008-07-17 19:37:38 +02:00
self.raise_user_error(cursor, 'del_draft_cancel',
context=context)
2008-07-25 12:49:38 +02:00
return super(Move, self).delete(cursor, user, ids, context=context)
2008-04-08 14:27:53 +02:00
Move()