Improved:

Sales Management
	Project Manegement
	Stock Management
	Accounting

bzr revid: fp@tinyerp.com-20080907232439-bod5fedw3o7w7u47
This commit is contained in:
Fabien Pinckaers 2008-09-08 01:24:39 +02:00
parent 426bc5164e
commit c949d120fc
28 changed files with 697 additions and 383 deletions

View File

@ -159,15 +159,6 @@ class account_account(osv.osv):
return super(account_account,self).search(cr, uid, args, offset, limit,
order, context=context, count=count)
# def _credit(self, cr, uid, ids, field_name, arg, context={}):
# return self.__compute(cr, uid, ids, field_name, arg, context, 'COALESCE(SUM(l.credit), 0)')
#
# def _debit(self, cr, uid, ids, field_name, arg, context={}):
# return self.__compute(cr, uid, ids, field_name, arg, context, 'COALESCE(SUM(l.debit), 0)')
#
# def _balance(self, cr, uid, ids, field_name, arg, context={}):
# return self.__compute(cr, uid, ids, field_name, arg, context, 'COALESCE(SUM(l.debit) - SUM(l.credit), 0)')
def __compute(self, cr, uid, ids, field_names, arg, context={}, query=''):
mapping = {
'balance': "COALESCE(SUM(l.debit) - SUM(l.credit), 0) as balance ",
@ -180,7 +171,7 @@ class account_account(osv.osv):
if ids2:
query = self.pool.get('account.move.line')._query_get(cr, uid,
context=context)
cr.execute(("SELECT l.account_id, " +\
cr.execute(("SELECT l.account_id as id, " +\
' , '.join(map(lambda x: mapping[x], field_names)) +
"FROM " \
"account_move_line l " \
@ -189,19 +180,16 @@ class account_account(osv.osv):
"AND " + query + " " \
"GROUP BY l.account_id") % (acc_set, ))
for res in cr.fetchall():
accounts[res[0]] = res[1:]
for res in cr.dictfetchall():
accounts[res['id']] = res
res = {}
for id in ids:
res[id] = map(lambda x: 0.0, field_names)
res[id] = {}.fromkeys(field_names, 0.0)
ids2 = self.search(cr, uid, [('parent_id', 'child_of', [id])])
for i in ids2:
for a in range(len(field_names)):
res[id][a] += accounts.get(i, (0.0,0.0,0.0))[a]
# TODO: if account.type is consolidation: compute all childs like before +
# currency conversion
for a in field_names:
res[id][a] += accounts.get(i, {}).get(a, 0.0)
return res
def _get_company_currency(self, cr, uid, ids, field_name, arg, context={}):
@ -233,6 +221,7 @@ class account_account(osv.osv):
('payable','Payable'),
('view','View'),
('consolidation','Consolidation'),
('other','Others'),
('closed','Closed'),
], 'Internal Type', required=True,),
@ -474,8 +463,8 @@ class account_fiscalyear(osv.osv):
while ds.strftime('%Y-%m-%d')<fy.date_stop:
de = ds + RelativeDateTime(months=interval, days=-1)
self.pool.get('account.period').create(cr, uid, {
'name': ds.strftime('%d/%m') + ' - '+de.strftime('%d/%m'),
'code': ds.strftime('%d/%m') + '-'+de.strftime('%d/%m'),
'name': ds.strftime('%m/%Y'),
'code': ds.strftime('%m/%Y'),
'date_start': ds.strftime('%Y-%m-%d'),
'date_stop': de.strftime('%Y-%m-%d'),
'fiscalyear_id': fy.id,

View File

@ -5,10 +5,6 @@
<test expr="not len(line_id) or line_id[0].state != 'valid' or (sum([l.debit - l.credit for l in line_id]) &lt;= 0.00001)"/>
</assert>
<!--assert model="account.invoice" search="[]" string="If the invoice is paid, third party accounting lines must be reconciled">
<test expr="not state == 'paid' or ..." />
</assert-->
<assert model="account.account" search="[]" string="For all accounts, the balance is equal to the sum of the balance of its childs">
<test expr="not len(child_id) or (balance - sum([c.balance for c in child_id]) &lt;= 0.00001)"/>
</assert>
@ -29,4 +25,4 @@
<test expr="not parent_id or (code!='0')"/>
</assert>
</data>
</terp>
</terp>

View File

@ -319,7 +319,7 @@ class account_bank_statement(osv.osv):
cursor.execute('SELECT balance_end_real \
FROM account_bank_statement \
WHERE journal_id = %d \
ORDER BY date DESC LIMIT 1', (journal_id,))
ORDER BY date DESC,id DESC LIMIT 1', (journal_id,))
res = cursor.fetchone()
balance_start = res and res[0] or 0.0

View File

@ -1411,7 +1411,7 @@
<field name="type">ir.actions.act_window</field>
<field name="res_model">account.config.fiscalyear</field>
<field name="view_type">form</field>
<field name="view_mode">form</field>
<field name="view_mode">tree</field>
<field name="target">new</field>
</record>

View File

@ -49,7 +49,7 @@ your own chart of account.
<record id="cash" model="account.account">
<field name="name">Petty Cash</field>
<field name="code">x 570000</field>
<field name="type">cash</field>
<field name="type">other</field>
<field name="user_type" ref="account.account_type_cash_moves"/>
<field eval="ref('minimal_0')" name="parent_id"/>
<field name="company_id" ref="base.main_company"/>
@ -64,7 +64,7 @@ your own chart of account.
<record id="a_expense" model="account.account">
<field name="name">Products Purchase</field>
<field name="code">x 600000</field>
<field name="type">expense</field>
<field name="type">other</field>
<field name="user_type" ref="account.account_type_expense"/>
<field eval="ref('minimal_0')" name="parent_id"/>
<field name="company_id" ref="base.main_company"/>
@ -78,7 +78,7 @@ your own chart of account.
<record id="a_sale" model="account.account">
<field name="name">Products Sales</field>
<field name="code">x 701000</field>
<field name="type">income</field>
<field name="type">other</field>
<field name="user_type" ref="account.account_type_income"/>
<field eval="ref('minimal_0')" name="parent_id"/>
<field name="company_id" ref="base.main_company"/>

View File

@ -55,13 +55,12 @@ class res_partner(osv.osv):
'credit': 'receivable',
'debit': 'payable'
}
maps = {}
for i in range(len(field_names)):
maps[{'credit': 'receivable', 'debit': 'payable' }[field_names[i]]] = i
res = {}.fromkeys(ids, map(lambda x: 0.0, field_names))
maps = {'receivable':'credit', 'payable':'debit' }
res = {}
for id in ids:
res[id] = {}.fromkeys(field_names, 0)
for pid,type,val in cr.fetchall():
if type in maps:
res[pid][maps[type]] = val
res[pid][maps[type]] = val
return res
def _credit_search(self, cr, uid, obj, name, args):

View File

@ -667,11 +667,16 @@ class hr_timesheet_sheet_sheet_account(osv.osv):
hr_timesheet_sheet_sheet_account()
class res_company(osv.osv):
_inherit = 'res.company'
_columns = {
'timesheet_range': fields.selection([('day','Day'),('week','Week'),('month','Month'),('year','Year')], 'Timeshet range'),
'timesheet_max_difference': fields.float('Timesheet allowed difference', help="Allowed difference between the sign in/out and the timesheet computation for one sheet. Set this to 0 if you do not want any control."),
'timesheet_range': fields.selection(
[('day','Day'),('week','Week'),('month','Month'),('year','Year')], 'Timeshet range',
required=True),
'timesheet_max_difference': fields.float('Timesheet allowed difference',
help="Allowed difference between the sign in/out and the timesheet " \
"computation for one sheet. Set this to 0 if you do not want any control."),
}
_defaults = {
'timesheet_range': lambda *args: 'month',

View File

@ -150,16 +150,16 @@
<field name="total_difference" widget="float_time"/>
</page>
<!--
<page string="By account">
<field name="account_ids" colspan="4" nolabel="1">
<tree string="Analytic accounts">
<field name="name"/>
<field name="total" widget="float_time"/>
<field name="invoice_rate"/>
</tree>
</field>
</page>
-->
<page string="By account">
<field name="account_ids" colspan="4" nolabel="1">
<tree string="Analytic accounts">
<field name="name"/>
<field name="total" widget="float_time"/>
<field name="invoice_rate"/>
</tree>
</field>
</page>
-->
</notebook>
<field name="state"/>
<group col="4" colspan="2">
@ -238,8 +238,8 @@
<menuitem action="act_hr_timesheet_sheet_form_all_valid" id="menu_act_hr_timesheet_sheet_form_all_valid" parent="hr_timesheet_sheet.menu_act_hr_timesheet_sheet_form"/>
<!--
Company inheritancy
-->
Company inheritancy
-->
<record id="hr_timesheet_sheet_company" model="ir.ui.view">
<field name="name">res.company.sheet</field>
@ -247,18 +247,18 @@
<field name="type">form</field>
<field name="inherit_id" ref="base.view_company_form"/>
<field name="arch" type="xml">
<field name="rml_footer2" position="after">
<newline/>
<page string="Configuration" position="inside">
<separator string="Timesheets" colspan="4"/>
<field name="timesheet_range"/>
<field name="timesheet_max_difference"/>
<newline/>
</field>
</page>
</field>
</record>
<!--
hr.analytic.timesheet inheritancy
-->
hr.analytic.timesheet inheritancy
-->
<record id="hr_timesheet_line_form" model="ir.ui.view">
<field name="name">hr.analytic.timesheet.form</field>
@ -271,10 +271,9 @@
</field>
</field>
</record>
<!--
hr.attendance inheritancy
-->
hr.attendance inheritancy
-->
<record id="view_attendance_form" model="ir.ui.view">
<field name="name">hr.attendance.form</field>

View File

@ -1124,11 +1124,11 @@ class StockPicking(osv.osv):
#
# Explode picking by replacing phantom BoMs
#
def action_explode(self, cr, uid, ids, *args):
for pick in self.browse(cr, uid, ids):
def action_explode(self, cr, uid, picks, *args):
for pick in picks:
for move in pick.move_lines:
self.pool.get('stock.move')._action_explode(cr, uid, move)
return True
return picks
StockPicking()

View File

@ -260,8 +260,8 @@ class product_template(osv.osv):
'warranty': fields.float('Warranty (months)'),
'sale_ok': fields.boolean('Can be sold', help="Determine if the product can be visible in the list of product within a selection from a sale order line."),
'purchase_ok': fields.boolean('Can be Purchased', help="Determine if the product is visible in the list of products within a selection from a purchase order line."),
'uom_id': fields.many2one('product.uom', 'Default UOM', required=True),
'uom_po_id': fields.many2one('product.uom', 'Purchase UOM', required=True),
'uom_id': fields.many2one('product.uom', 'Default UoM', required=True, help="This is the default Unit of Measure used for all stock operation."),
'uom_po_id': fields.many2one('product.uom', 'Purchase UoM', required=True),
'state': fields.selection([('',''),('draft', 'In Development'),('sellable','In Production'),('end','End of Lifecycle'),('obsolete','Obsolete')], 'Status', help="Tells the user if he can use the product or not."),
'uos_id' : fields.many2one('product.uom', 'Unit of Sale',
help='Keep empty to use the default UOM'),
@ -347,10 +347,7 @@ class product_product(osv.osv):
def _get_product_available_func(states, what):
def _product_available(self, cr, uid, ids, name, arg, context={}):
res={}
for id in ids:
res.setdefault(id, 0.0)
return res
return {}.fromkeys(ids, 0.0)
return _product_available
_product_qty_available = _get_product_available_func(('done',), ('in', 'out'))

View File

@ -170,7 +170,7 @@
<field name="name">product.category.list</field>
<field name="model">product.category</field>
<field name="type">tree</field>
<field name="sequence">1</field>
<field name="priority">1</field>
<field name="arch" type="xml">
<tree string="Product Categories">
<field name="complete_name"/>

View File

@ -26,7 +26,9 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
###############################################################################
import project
import company
import report
import wizard

50
addons/project/company.py Normal file
View File

@ -0,0 +1,50 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2004-2008 TINY SPRL. (http://tiny.be) All Rights Reserved.
#
# $Id$
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
from osv import fields
from osv import osv
class res_company(osv.osv):
_inherit = 'res.company'
_columns = {
'project_time_mode': fields.selection(
[('hours','Hours'),('day','Days'),('week','Weeks'),('month','Months')],
'Project Time Unit',
help='This will set the unit of measure used in projects and tasks.\n' \
"If you use the timesheet linked to projects (project_timesheet module), don't " \
"forget to setup the right unit of measure in your employees.",
required=True,
),
}
_defaults = {
'project_time_mode': lambda *args: 'hours',
}
res_company()

View File

@ -28,13 +28,12 @@
#
##############################################################################
from lxml import etree
from mx import DateTime
from mx.DateTime import now
import time
import netsvc
from osv import fields, osv
import ir
class project(osv.osv):
_name = "project.project"
@ -65,7 +64,7 @@ class project(osv.osv):
ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)])
res_sum = {}
if ids2:
cr.execute('SELECT project_id, COALESCE(SUM(planned_hours),0) \
cr.execute('SELECT project_id, COALESCE(SUM(total_hours),0) \
FROM project_task \
WHERE project_id IN (' + ','.join([str(x) for x in ids2]) + ') \
AND active \
@ -97,7 +96,7 @@ class project(osv.osv):
if not ids:
return res
cr.execute('''SELECT
project_id, sum(progress*planned_hours), sum(planned_hours)
project_id, sum(progress*total_hours), sum(total_hours)
FROM
project_task
WHERE
@ -132,7 +131,7 @@ class project(osv.osv):
'warn_header': fields.text('Mail header'),
'warn_footer': fields.text('Mail footer'),
'notes': fields.text('Notes'),
'timesheet_id': fields.many2one('hr.timesheet.group', 'Working hours'),
'timesheet_id': fields.many2one('hr.timesheet.group', 'Working Time'),
'state': fields.selection([('open', 'Open'),('pending', 'Pending'), ('cancelled', 'Cancelled'), ('done', 'Done')], 'Status', required=True),
}
@ -203,16 +202,30 @@ class task(osv.osv):
t3 += map(lambda x: (x,t2[1]+1), t2[0].child_ids)
return result
def _hours_effect(self, cr, uid, ids, name, args, context):
# Compute: effective_hours, total_hours, progress
def _hours_get(self, cr, uid, ids, field_names, args, context):
task_set = ','.join(map(str, ids))
cr.execute(("SELECT task_id, COALESCE(SUM(hours),0) FROM project_task_work WHERE task_id in (%s) GROUP BY task_id") % (task_set,))
hours = dict(cr.fetchall())
res = {}
for id in ids:
res[id] = 0.0
for task_id, sum in cr.fetchall():
res[task_id] = sum
for task in self.browse(cr, uid, ids, context=context):
res[task.id] = {}
res[task.id]['effective_hours'] = hours.get(task.id, 0.0)
res[task.id]['total_hours'] = task.remaining_hours + hours.get(task.id, 0.0)
if (task.remaining_hours + hours.get(task.id, 0.0)):
res[task.id]['progress'] = min(100.0 * hours.get(task.id, 0.0) / res[task.id]['total_hours'], 100)
else:
res[task.id]['progress'] = 0.0
res[task.id]['delay_hours'] = res[task.id]['total_hours'] - task.planned_hours
return res
def onchange_planned(self, cr, uid, ids, planned, effective):
return {'value':{'remaining_hours': planned-effective}}
#_sql_constraints = [
# ('remaining_hours', 'CHECK (remaining_hours>=0)', 'Please increase and review remaining hours ! It can not be smaller than 0.'),
#]
_columns = {
'active': fields.boolean('Active'),
'name': fields.char('Task summary', size=128, required=True),
@ -220,7 +233,7 @@ class task(osv.osv):
'priority' : fields.selection([('4','Very Low'), ('3','Low'), ('2','Medium'), ('1','Urgent'), ('0','Very urgent')], 'Importance'),
'sequence': fields.integer('Sequence'),
'type': fields.many2one('project.task.type', 'Type'),
'state': fields.selection([('draft', 'Draft'),('open', 'Open'),('pending', 'Pending'), ('cancelled', 'Cancelled'), ('done', 'Done')], 'Status'),
'state': fields.selection([('draft', 'Draft'),('open', 'Open'),('pending', 'Pending'), ('cancelled', 'Cancelled'), ('done', 'Done')], 'Status', readonly=True, required=True),
'date_start': fields.datetime('Date'),
'date_deadline': fields.datetime('Deadline'),
'date_close': fields.datetime('Date Closed', readonly=True),
@ -230,16 +243,21 @@ class task(osv.osv):
'history': fields.function(_history_get, method=True, string="Task Details", type="text"),
'notes': fields.text('Notes'),
'start_sequence': fields.boolean('Wait for previous sequences'),
'planned_hours': fields.float('Plan. hours'),
'effective_hours': fields.function(_hours_effect, method=True, string='Eff. Hours'),
'progress': fields.integer('Progress (0-100)'),
'planned_hours': fields.float('Planned Hours', readonly=True, states={'draft':[('readonly',False)]}),
'effective_hours': fields.function(_hours_get, method=True, string='Hours Spent', multi='hours', store=True),
'remaining_hours': fields.float('Remaining Hours', digits=(16,2)),
'total_hours': fields.function(_hours_get, method=True, string='Total Hours', multi='hours', store=True),
'progress': fields.function(_hours_get, method=True, string='Progress (%)', multi='hours', store=True),
'delay_hours': fields.function(_hours_get, method=True, string='Delay Hours', multi='hours', store=True),
'user_id': fields.many2one('res.users', 'Assigned to'),
'partner_id': fields.many2one('res.partner', 'Partner'),
'work_ids': fields.one2many('project.task.work', 'task_id', 'Work done'),
'work_ids': fields.one2many('project.task.work', 'task_id', 'Work done', readonly=False, states={'draft':[('readonly',True)]}),
}
_defaults = {
'user_id': lambda obj,cr,uid,context: uid,
'state': lambda *a: 'open',
'state': lambda *a: 'draft',
'priority': lambda *a: '2',
'progress': lambda *a: 0,
'sequence': lambda *a: 10,
@ -249,6 +267,31 @@ class task(osv.osv):
}
_order = "state, sequence, priority, date_deadline, id"
#
# Override view according to the company definition
#
def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False):
tm = self.pool.get('res.users').browse(cr, uid, uid, context).company_id.project_time_mode
f = self.pool.get('res.company').fields_get(cr, uid, ['project_time_mode'], context)
word = dict(f['project_time_mode']['selection'])[tm]
res = super(task, self).fields_view_get(cr, uid, view_id, view_type, context, toolbar)
if tm=='hours':
return res
eview = etree.fromstring(res['arch'])
def _check_rec(eview, tm):
if eview.attrib.get('widget',False) == 'float_time':
eview.set('widget','float')
for child in eview:
_check_rec(child, tm)
return True
_check_rec(eview, tm)
res['arch'] = etree.tostring(eview)
for f in res['fields']:
if 'Hours' in res['fields'][f]['string']:
res['fields'][f]['string'] = res['fields'][f]['string'].replace('Hours',word)
return res
def do_close(self, cr, uid, ids, *args):
request = self.pool.get('res.request')
tasks = self.browse(cr, uid, ids)
@ -265,7 +308,7 @@ class task(osv.osv):
'ref_doc1': 'project.task,%d'% (task.id,),
'ref_doc2': 'project.project,%d'% (project.id,),
})
self.write(cr, uid, [task.id], {'state': 'done', 'date_close':time.strftime('%Y-%m-%d %H:%M:%S'), 'progress': 100})
self.write(cr, uid, [task.id], {'state': 'done', 'date_close':time.strftime('%Y-%m-%d %H:%M:%S'), 'remaining_hours': 0.0})
if task.parent_id and task.parent_id.state in ('pending','draft'):
self.do_reopen(cr, uid, [task.parent_id.id])
return True
@ -304,7 +347,7 @@ class task(osv.osv):
'ref_doc1': 'project.task,%d' % task.id,
'ref_doc2': 'project.project,%d' % project.id,
})
self.write(cr, uid, [task.id], {'state': 'cancelled', 'progress':100})
self.write(cr, uid, [task.id], {'state': 'cancelled', 'remaining_hours':0.0})
return True
def do_open(self, cr, uid, ids, *args):
@ -331,7 +374,7 @@ class project_work(osv.osv):
_columns = {
'name': fields.char('Work summary', size=128),
'date': fields.datetime('Date'),
'task_id': fields.many2one('project.task', 'Task', ondelete='cascade'),
'task_id': fields.many2one('project.task', 'Task', ondelete='cascade', required=True),
'hours': fields.float('Hours spent'),
'user_id': fields.many2one('res.users', 'Done by', required=True),
}
@ -340,6 +383,20 @@ class project_work(osv.osv):
'date': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S')
}
_order = "date desc"
def create(self, cr, uid, vals, *args, **kwargs):
if 'task_id' in vals:
cr.execute('update project_task set remaining_hours=remaining_hours+%.2f where id=%d', (-vals.get('hours',0.0), vals['task_id']))
return super(project_work,self).create(cr, uid, vals, *args, **kwargs)
def write(self, cr, uid, ids,vals,context={}):
for work in self.browse(cr, uid, ids, context):
cr.execute('update project_task set remaining_hours=remaining_hours+%.2f+(%.2f) where id=%d', (-vals.get('hours',0.0), work.hours, work.task_id.id))
return super(project_work,self).write(cr, uid, ids, vals, context)
def unlink(self, cr, uid, ids, *args, **kwargs):
for work in self.browse(cr, uid, ids):
cr.execute('update project_task set remaining_hours=remaining_hours+%.2f where id=%d', (work.hours, work.task_id.id))
return super(project_work,self).unlink(cr, uid, ids,*args, **kwargs)
project_work()

View File

@ -26,12 +26,12 @@
<field name="active" select="2"/>
<button name="toggleActive" string="Toggle activity" type="object"/>
</group>
<field name="warn_manager"/>
<newline/>
<field name="planned_hours" widget="float_time"/>
<field name="effective_hours" widget="float_time"/>
<field name="warn_manager"/>
<field name="timesheet_id"/>
<field name="state"/>
<newline/>
<separator colspan="4" string="Project's members"/>
<field colspan="4" name="members" nolabel="1"/>
</page>
@ -209,18 +209,25 @@
<field eval="2" name="priority"/>
<field name="arch" type="xml">
<form string="Task edition">
<field name="name" select="1"/>
<field name="project_id" required="1" select="1"/>
<field name="user_id" select="1"/>
<field name="planned_hours" widget="float_time"/>
<group colspan="6" col="6">
<field name="name" select="1"/>
<field name="project_id" required="1" select="1"/>
<field name="total_hours" widget="float_time"/>
<field name="user_id" select="1"/>
<field name="date_deadline" select="2"/>
<field name="progress" widget="progressbar"/>
</group>
<notebook colspan="4">
<page string="Information">
<field name="date_deadline" select="2"/>
<field name="priority"/>
<separator colspan="4" string="Description"/>
<field colspan="4" name="description" nolabel="1" select="2"/>
<field
name="planned_hours"
widget="float_time"
on_change="onchange_planned(planned_hours,effective_hours)"/>
<field name="delay_hours" widget="float_time"/>
<field name="remaining_hours" select="2" widget="float_time"/>
<field name="effective_hours" widget="float_time"/>
<field name="progress"/>
<field colspan="4" name="description" nolabel="1" select="2"/>
<field colspan="4" name="work_ids" nolabel="1"/>
<newline/>
<group col="11" colspan="4">
@ -250,10 +257,14 @@
<field colspan="4" name="parent_id"/>
</page>
<page groups="base.group_extended" string="Extra Info">
<separator string="Planning" colspan="2"/>
<separator string="Dates" colspan="2"/>
<field name="priority"/>
<field name="date_start" select="2"/>
<field name="date_close" select="2"/>
<field name="type"/>
<field name="sequence"/>
<field name="date_close" select="2"/>
<field name="type"/>
<field name="active" select="2"/>
<field name="start_sequence"/>
<field name="partner_id" select="2"/>
@ -270,17 +281,18 @@
<field name="type">tree</field>
<field eval="2" name="priority"/>
<field name="arch" type="xml">
<tree colors="grey:state in ('cancel','done');blue:state=='pending'" string="Tasks">
<tree colors="grey:state in ('cancel','done');blue:remaining_hours&lt;0;red:bool(date_deadline) &amp; (date_deadline&lt;current_date) &amp; (state in ('draft','open'))" string="Tasks">
<field name="sequence"/>
<field name="name"/>
<field name="project_id"/>
<field name="user_id"/>
<field name="date_deadline"/>
<field name="planned_hours" sum="Planned hours" widget="float_time"/>
<field name="effective_hours" sum="Effective hours" widget="float_time"/>
<field name="planned_hours" sum="Planned" widget="float_time"/>
<field name="delay_hours" sum="Delay" widget="float_time"/>
<field name="progress" widget="progressbar"/>
<field name="priority"/>
<field name="state"/>
<field name="remaining_hours" invisible="1"/>
</tree>
</field>
</record>
@ -444,6 +456,20 @@
view_mode="tree,form,calendar"
view_type="form"/>
<record id="task_company" model="ir.ui.view">
<field name="name">res.company.task.config</field>
<field name="model">res.company</field>
<field name="type">form</field>
<field name="inherit_id" ref="base.view_company_form"/>
<field name="arch" type="xml">
<page string="Configuration" position="inside">
<separator string="Project Management" colspan="4"/>
<field name="project_time_mode"/>
<newline/>
</page>
</field>
</record>
<act_window domain="[('user_id', '=', active_id),('state', '&lt;&gt;', 'cancelled'),('state', '&lt;&gt;', 'done')]" id="act_res_users_2_project_task_opened" name="Assigned tasks" res_model="project.task" src_model="res.users" view_mode="tree,form" view_type="form"/>
<act_window domain="[('user_id', '=', active_id),('date', '&gt;=', time.strftime('%Y-%m-01'))]" id="act_res_users_2_project_task_work_month" name="Month works" res_model="project.task.work" src_model="res.users" view_mode="tree,form" view_type="form"/>

View File

@ -1,8 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<terp>
<data>
<wizard id="wizard_billing" model="project.task" name="project.wiz_bill" string="Bill tasks"/>
<wizard id="wizard_close_task" menu="False" model="project.task" name="project.task.close" string="Close Task"/>
<wizard id="wizard_delegate_task" menu="False" model="project.task" name="project.task.delegate" string="Delegate Task"/>
</data>
</terp>
</terp>

View File

@ -620,13 +620,6 @@ class sale_order_line(osv.osv):
res[line.id] = 1
return res
def _get_1st_packaging(self, cr, uid, context={}):
cr.execute('select id from product_packaging order by id asc limit 1')
res = cr.fetchone()
if not res:
return False
return res[0]
_name = 'sale.order.line'
_description = 'Sale Order line'
_columns = {
@ -667,7 +660,7 @@ class sale_order_line(osv.osv):
'invoiced': lambda *a: 0,
'state': lambda *a: 'draft',
'type': lambda *a: 'make_to_stock',
'product_packaging': _get_1st_packaging,
'product_packaging': lambda *a: False
}
def invoice_line_create(self, cr, uid, ids, context={}):
def _get_line_qty(line):
@ -766,7 +759,7 @@ class sale_order_line(osv.osv):
def product_id_change(self, cr, uid, ids, pricelist, product, qty=0,
uom=False, qty_uos=0, uos=False, name='', partner_id=False,
lang=False, update_tax=True, date_order=False):
lang=False, update_tax=True, date_order=False, packaging=False):
product_uom_obj = self.pool.get('product.uom')
partner_obj = self.pool.get('res.partner')
product_obj = self.pool.get('product.product')
@ -776,7 +769,7 @@ class sale_order_line(osv.osv):
context = {'lang': lang, 'partner_id': partner_id}
if not product:
return {'value': {'th_weight' : 0,
return {'value': {'th_weight' : 0, 'product_packaging': False,
'product_uos_qty': qty}, 'domain': {'product_uom': [],
'product_uos': []}}
@ -787,6 +780,16 @@ class sale_order_line(osv.osv):
if not date_order:
date_order = time.strftime('%Y-%m-%d')
result = {}
product_obj = product_obj.browse(cr, uid, product, context=context)
if packaging:
default_uom = product_obj.uom_id and product_obj.uom_id.id
pack = self.pool.get('product.packaging').browse(cr, uid, packaging, context)
q = product_uom_obj._compute_qty(cr, uid, uom, pack.qty, default_uom)
qty = qty - qty % q + q
result['product_uom_qty'] = qty
price = self.pool.get('product.pricelist').price_get(cr, uid, [pricelist],
product, qty or 1.0, partner_id, {
'uom': uom,
@ -797,35 +800,34 @@ class sale_order_line(osv.osv):
_("Couldn't find a pricelist line matching this product and quantity.\n"
"You have to change either the product, the quantity or the pricelist."))
product = product_obj.browse(cr, uid, product, context=context)
if uom:
uom2 = product_uom_obj.browse(cr, uid, uom)
if product.uom_id.category_id.id <> uom2.category_id.id:
if product_obj.uom_id.category_id.id <> uom2.category_id.id:
uom = False
if uos:
if product.uos_id:
if product_obj.uos_id:
uos2 = product_uom_obj.browse(cr, uid, uos)
if product.uos_id.category_id.id <> uos2.category_id.id:
if product_obj.uos_id.category_id.id <> uos2.category_id.id:
uos = False
else:
uos = False
result = {'price_unit': price, 'type': product.procure_method}
if product.description_sale:
result['notes'] = product.description_sale
result .update({'price_unit': price, 'type': product_obj.procure_method})
if product_obj.description_sale:
result['notes'] = product_obj.description_sale
if update_tax: #The quantity only have changed
result['delay'] = (product.sale_delay or 0.0)
result['delay'] = (product_obj.sale_delay or 0.0)
taxes = self.pool.get('account.tax').browse(cr, uid,
[x.id for x in product.taxes_id])
[x.id for x in product_obj.taxes_id])
taxep = None
if partner_id:
taxep = self.pool.get('res.partner').browse(cr, uid,
partner_id).property_account_tax
if not taxep or not taxep.id:
result['tax_id'] = [x.id for x in product.taxes_id]
result['tax_id'] = [x.id for x in product_obj.taxes_id]
else:
res5 = [taxep.id]
for t in taxes:
@ -833,38 +835,39 @@ class sale_order_line(osv.osv):
res5.append(t.id)
result['tax_id'] = res5
result['name'] = product.partner_ref
result['name'] = product_obj.partner_ref
domain = {}
if not uom and not uos:
result['product_uom'] = product.uom_id.id
if product.uos_id:
result['product_uos'] = product.uos_id.id
result['product_uos_qty'] = qty * product.uos_coeff
uos_category_id = product.uos_id.category_id.id
result['product_uom'] = product_obj.uom_id.id
if product_obj.uos_id:
result['product_uos'] = product_obj.uos_id.id
result['product_uos_qty'] = qty * product_obj.uos_coeff
uos_category_id = product_obj.uos_id.category_id.id
else:
result['product_uos'] = False
result['product_uos_qty'] = qty
uos_category_id = False
result['th_weight'] = qty * product.weight
result['th_weight'] = qty * product_obj.weight
domain = {'product_uom':
[('category_id', '=', product.uom_id.category_id.id)],
[('category_id', '=', product_obj.uom_id.category_id.id)],
'product_uos':
[('category_id', '=', uos_category_id)]}
elif uom: # whether uos is set or not
default_uom = product.uom_id and product.uom_id.id
default_uom = product_obj.uom_id and product_obj.uom_id.id
q = product_uom_obj._compute_qty(cr, uid, uom, qty, default_uom)
if product.uos_id:
result['product_uos'] = product.uos_id.id
result['product_uos_qty'] = q * product.uos_coeff
if product_obj.uos_id:
result['product_uos'] = product_obj.uos_id.id
result['product_uos_qty'] = q * product_obj.uos_coeff
else:
result['product_uos'] = False
result['product_uos_qty'] = q
result['th_weight'] = q * product.weight
result['th_weight'] = q * product_obj.weight
elif uos: # only happens if uom is False
result['product_uom'] = product.uom_id and product.uom_id.id
result['product_uom_qty'] = qty_uos / product.uos_coeff
result['th_weight'] = result['product_uom_qty'] * product.weight
result['product_uom'] = product_obj.uom_id and product_obj.uom_id.id
result['product_uom_qty'] = qty_uos / product_obj.uos_coeff
result['th_weight'] = result['product_uom_qty'] * product_obj.weight
# Round the quantity up
return {'value': result, 'domain': domain}
def product_uom_change(self, cursor, user, ids, pricelist, product, qty=0,

View File

@ -91,9 +91,24 @@
<notebook>
<page string="Order Line">
<separator colspan="4" string="Automatic Declaration"/>
<field colspan="4" context="partner_id=parent.partner_id,quantity=product_uom_qty,pricelist=parent.pricelist_id,shop=parent.shop_id,uom=product_uom" name="product_id" on_change="product_id_change(parent.pricelist_id,product_id,product_uom_qty,product_uom,product_uos_qty,product_uos,name,parent.partner_id, 'lang' in context and context['lang'], True, parent.date_order)" select="1"/>
<field context="partner_id=parent.partner_id,quantity=product_uom_qty,pricelist=parent.pricelist_id,shop=parent.shop_id,uom=product_uom" name="product_uom_qty" on_change="product_id_change(parent.pricelist_id,product_id,product_uom_qty,product_uom,product_uos_qty,product_uos,name,parent.partner_id, 'lang' in context and context['lang'], False, parent.date_order)" select="1"/>
<field name="product_uom" on_change="product_uom_change(parent.pricelist_id,product_id,product_uom_qty,product_uom,product_uos_qty,product_uos,name,parent.partner_id, 'lang' in context and context['lang'], False, parent.date_order)"/>
<field colspan="4"
context="partner_id=parent.partner_id,quantity=product_uom_qty,pricelist=parent.pricelist_id,shop=parent.shop_id,uom=product_uom"
name="product_id"
on_change="product_id_change(parent.pricelist_id,product_id,product_uom_qty,product_uom,product_uos_qty,product_uos,name,parent.partner_id, 'lang' in context and context['lang'], True, parent.date_order, product_packaging)"
select="1"/>
<field
context="partner_id=parent.partner_id,quantity=product_uom_qty,pricelist=parent.pricelist_id,shop=parent.shop_id,uom=product_uom"
name="product_uom_qty"
on_change="product_id_change(parent.pricelist_id,product_id,product_uom_qty,product_uom,product_uos_qty,product_uos,name,parent.partner_id, 'lang' in context and context['lang'], False, parent.date_order, product_packaging)"
select="1"/>
<field name="product_uom"
on_change="product_uom_change(parent.pricelist_id,product_id,product_uom_qty,product_uom,product_uos_qty,product_uos,name,parent.partner_id, 'lang' in context and context['lang'], False, parent.date_order)"/>
<field
name="product_packaging"
context="partner_id=parent.partner_id,quantity=product_uom_qty,pricelist=parent.pricelist_id,shop=parent.shop_id,uom=product_uom"
on_change="product_id_change(parent.pricelist_id,product_id,product_uom_qty,product_uom,product_uos_qty,product_uos,name,parent.partner_id, 'lang' in context and context['lang'], False, parent.date_order, product_packaging)"
domain="[('product_id','=',product_id)]"
groups="base.group_extended"/>
<separator colspan="4" string="Manual Description"/>
<field colspan="4" name="name" select="2"/>
<field name="price_unit" select="2"/>
@ -112,7 +127,6 @@
<page groups="base.group_extended" string="Extra Info">
<field groups="product.group_uos" name="product_uos_qty" on_change="uos_change(product_uos, product_uos_qty, product_id)"/>
<field groups="product.group_uos" name="product_uos"/>
<field name="product_packaging"/>
<field name="address_allotment_id" select="2"/>
<separator colspan="4" string="Properties"/>
<field colspan="4" name="property_ids" nolabel="1"/>
@ -209,7 +223,7 @@
<menuitem action="action_order_tree_all" id="menu_action_order_tree_all" parent="sale.menu_sale_order"/>
<record id="action_order_tree_new" model="ir.actions.act_window">
<field name="name">New Sale Order / Quotation</field>
<field name="name">New Quotation</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">sale.order</field>
<field name="view_type">form</field>

View File

@ -1,5 +1,5 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_sale_shop,sale.shop,model_sale_shop,sale.group_sale_user,1,0,0,0
access_sale_order,sale.order,model_sale_order,sale.group_sale_user,1,1,1,1
access_sale_order_line,sale.order.line,model_sale_order_line,sale.group_sale_user,1,1,1,1
access_sale_shop,sale.shop,model_sale_shop,group_sale_user,1,0,0,0
access_sale_order,sale.order,model_sale_order,group_sale_user,1,1,1,1
access_sale_order_line,sale.order.line,model_sale_order_line,group_sale_user,1,1,1,1
access_sale_shop_admin,sale.shop,model_sale_shop,base.group_system,1,1,1,1

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_sale_shop sale.shop model_sale_shop sale.group_sale_user group_sale_user 1 0 0 0
3 access_sale_order sale.order model_sale_order sale.group_sale_user group_sale_user 1 1 1 1
4 access_sale_order_line sale.order.line model_sale_order_line sale.group_sale_user group_sale_user 1 1 1 1
5 access_sale_shop_admin sale.shop model_sale_shop base.group_system 1 1 1 1

View File

@ -30,6 +30,60 @@
from osv import fields, osv
class product_product(osv.osv):
_inherit = "product.product"
# def view_header_get(self, cr, user, view_id, view_type, context):
# print self, cr, user
# res = super(product_product, self).view_header_get(cr, user, view_id, view_type, context)
# if res: return res
# if (not context.get('location', False)):
# return False
# cr.execute('select name from stock_location where id=%d', (context['location'],))
# j = cr.fetchone()[0]
# if j:
# return 'Products: '+j
# return False
#
def _get_product_available_func(states, what):
def _product_available(self, cr, uid, ids, name, arg, context={}):
if context.get('shop', False):
cr.execute('select warehouse_id from sale_shop where id=%d', (int(context['shop']),))
res2 = cr.fetchone()
if res2:
context['warehouse'] = res2[0]
if context.get('warehouse', False):
cr.execute('select lot_stock_id from stock_warehouse where id=%d', (int(context['warehouse']),))
res2 = cr.fetchone()
if res2:
context['location'] = res2[0]
if context.get('location', False):
location_ids = [context['location']]
else:
cr.execute("select lot_stock_id from stock_warehouse")
location_ids = [id for (id,) in cr.fetchall()]
# build the list of ids of children of the location given by id
location_ids = self.pool.get('stock.location').search(cr, uid, [('location_id', 'child_of', location_ids)])
res = self.pool.get('stock.location')._product_get_multi_location(cr, uid, location_ids, ids, context, states, what)
for id in ids:
res.setdefault(id, 0.0)
return res
return _product_available
_product_qty_available = _get_product_available_func(('done',), ('in', 'out'))
_product_virtual_available = _get_product_available_func(('confirmed','waiting','assigned','done'), ('in', 'out'))
_product_outgoing_qty = _get_product_available_func(('confirmed','waiting','assigned'), ('out',))
_product_incoming_qty = _get_product_available_func(('confirmed','waiting','assigned'), ('in',))
_columns = {
'qty_available': fields.function(_product_qty_available, method=True, type='float', string='Real Stock'),
'virtual_available': fields.function(_product_virtual_available, method=True, type='float', string='Virtual Stock'),
'incoming_qty': fields.function(_product_incoming_qty, method=True, type='float', string='Incoming'),
'outgoing_qty': fields.function(_product_outgoing_qty, method=True, type='float', string='Outgoing'),
}
product_product()
class product_product(osv.osv):
_name = 'product.template'

View File

@ -45,6 +45,7 @@
<field name="model">product.product</field>
<field name="type">form</field>
<field name="inherit_id" ref="product.product_normal_form_view"/>
<field name="priority">26</field>
<field name="arch" type="xml">
<field name="property_account_expense" position="after">
<field name="property_stock_account_input"/>

View File

@ -53,21 +53,6 @@ class stock_incoterms(osv.osv):
}
stock_incoterms()
#class stock_lot(osv.osv):
# _name = "stock.lot"
# _description = "Lot"
# _columns = {
# 'name': fields.char('Lot Name', size=64, required=True),
# 'active': fields.boolean('Active'),
# 'tracking': fields.char('Tracking', size=64),
# 'move_ids': fields.one2many('stock.move', 'lot_id', 'Move lines'),
# }
# _defaults = {
# 'active': lambda *a: True,
# }
#stock_lot()
#----------------------------------------------------------
# Stock Location
#----------------------------------------------------------
@ -87,21 +72,30 @@ class stock_location(osv.osv):
'chained_location_id': fields.many2one('stock.location', 'Chained Location If Fixed'),
'chained_location_type': fields.selection([('','None'),('customer', 'Customer'),('fixed','Fixed Location')], 'Chained Location Type'),
'chained_auto_packing': fields.boolean('Chained Auto-Packing'),
'chained_auto_packing': fields.selection(
[('auto','Automatic Move'), ('manual','Manual Operation'),('transparent','Automatic No Step Added')],
'Automatic Move',
required=True, select=1,
help="This is used only if you selected a chained location type.\n" \
"The 'Automatic Move' value will create a stock move after the current one that will be "\
"validated automatically. With 'Manual Operation', the stock move has to be validated "\
"by a worker. With 'Automatic No Step Added', the location is replaced in the original move."
),
'chained_delay': fields.integer('Chained Delay (days)'),
'address_id': fields.many2one('res.partner.address', 'Location Address'),
'comment': fields.text('Additional Information'),
'posx': fields.integer('Corridor (X)', required=True),
'posy': fields.integer('Shelves (Y)', required=True),
'posz': fields.integer('Height (Z)', required=True),
'posx': fields.integer('Corridor (X)'),
'posy': fields.integer('Shelves (Y)'),
'posz': fields.integer('Height (Z)'),
}
_defaults = {
'active': lambda *a: 1,
'usage': lambda *a: 'internal',
'allocation_method': lambda *a: 'fifo',
'chained_location_type': lambda *a: '',
'chained_auto_packing': lambda *a: 'manual',
'posx': lambda *a: 0,
'posy': lambda *a: 0,
'posz': lambda *a: 0,
@ -110,12 +104,14 @@ class stock_location(osv.osv):
def chained_location_get(self, cr, uid, location, partner=None, product=None, context={}):
result = None
if location.chained_location_type=='customer':
result = partner.property_stock_customer
if partner:
result = partner.property_stock_customer
elif location.chained_location_type=='fixed':
result = location.chained_location_id
if result:
return result, location.chained_auto_packing, location.chained_delay
return result
def picking_type_get(self, cr, uid, from_location, to_location, context={}):
result = 'internal'
if (from_location.usage=='internal') and (to_location and to_location.usage in ('customer','supplier')):
@ -175,9 +171,7 @@ class stock_location(osv.osv):
states_str = ','.join(map(lambda s: "'%s'" % s, states))
if not product_ids:
product_ids = product_obj.search(cr, uid, [])
res = {}
for id in product_ids:
res[id] = 0.0
res = {}.fromkeys(product_ids, 0.0)
if not ids:
return res
@ -332,15 +326,14 @@ class stock_picking(osv.osv):
_name = "stock.picking"
_description = "Packing list"
_columns = {
'name': fields.char('Packing name', size=64, required=True, select=True),
'name': fields.char('Reference', size=64, required=True, select=True),
'origin': fields.char('Origin', size=64),
'type': fields.selection([('out','Sending Goods'),('in','Getting Goods'),('internal','Internal'),('delivery','Delivery')], 'Shipping Type', required=True, select=True),
'active': fields.boolean('Active'),
'note': fields.text('Notes'),
'location_id': fields.many2one('stock.location', 'Location'),
'location_dest_id': fields.many2one('stock.location', 'Dest. Location'),
#'location_id': fields.many2one('stock.location', 'Location'),
#'location_dest_id': fields.many2one('stock.location', 'Dest. Location'),
'move_type': fields.selection([('direct','Direct Delivery'),('one','All at once')],'Delivery Method', required=True),
'state': fields.selection([
('draft','Draft'),
@ -351,9 +344,7 @@ class stock_picking(osv.osv):
('cancel','Cancel'),
], 'Status', readonly=True, select=True),
'date':fields.datetime('Date Create'),
'move_lines': fields.one2many('stock.move', 'picking_id', 'Move Lines'),
'auto_picking': fields.boolean('Auto-Packing'),
'address_id': fields.many2one('res.partner.address', 'Partner'),
'invoice_state':fields.selection([
@ -363,7 +354,7 @@ class stock_picking(osv.osv):
select=True),
}
_defaults = {
'name': lambda *a: '/',
'name': lambda self,cr,uid,context: self.pool.get('ir.sequence').get(cr, uid, 'stock.picking'),
'active': lambda *a: 1,
'state': lambda *a: 'draft',
'move_type': lambda *a: 'direct',
@ -371,64 +362,28 @@ class stock_picking(osv.osv):
'invoice_state': lambda *a: 'none',
'date': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
}
def copy(self, cr, uid, id, data=None, context={}):
data = data or {}
data['name'] = '/'
return super(stock_picking, self).copy(cr, uid, id, data, context)
def onchange_partner_in(self, cr, uid, context, partner_id=None):
sid = self.pool.get('res.partner.address').browse(cr, uid, partner_id, context).partner_id.property_stock_supplier.id
return {
'value': {'location_id':sid}
}
return { }
def action_explode(self, cr, uid, ids, *args):
return True
def action_explode(self, cr, uid, moves, context={}):
return moves
def action_confirm(self, cr, uid, ids, *args):
def action_confirm(self, cr, uid, ids, context={}):
self.write(cr, uid, ids, {'state': 'confirmed'})
todo = []
for picking in self.browse(cr, uid, ids):
if picking.name == self._defaults['name']():
number = self.pool.get('ir.sequence').get(cr, uid, 'stock.picking.%s' % picking.type)
self.write(cr, uid, [picking.id], {'name': number})
for r in picking.move_lines:
if r.state=='draft':
todo.append(r.id)
todo.append(r)
todo = self.action_explode(cr, uid, todo, context)
if len(todo):
self.pool.get('stock.move').action_confirm(cr,uid, todo)
self.action_explode(cr, uid, ids)
for picking in self.browse(cr, uid, ids):
todo = []
todo.extend( filter( lambda r: r.location_dest_id.chained_location_type, picking.move_lines) )
if todo:
loc = self.pool.get('stock.location').chained_location_get(cr, uid, todo[0].location_dest_id, todo[0].picking_id and todo[0].picking_id.address_id and todo[0].picking_id.address_id.partner_id, todo[0].product_id)
ptype = self.pool.get('stock.location').picking_type_get(cr, uid, todo[0].location_dest_id, loc)
pickid = self.pool.get('stock.picking').create(cr, uid, {
'name': picking.name,
'origin': str(picking.origin or ''),
'type': ptype,
'note': picking.note,
'move_type': picking.move_type,
'auto_picking': todo[0].location_dest_id.chained_auto_packing,
'address_id': picking.address_id.id,
'invoice_state': 'none'
})
for move in todo:
loc = self.pool.get('stock.location').chained_location_get(cr, uid, move.location_dest_id, picking.address_id.partner_id, move.product_id).id
new_id = self.pool.get('stock.move').copy(cr, uid, move.id, {
'location_id': move.location_dest_id.id,
'location_dest_id': loc,
'date_moved': time.strftime('%Y-%m-%d'),
'picking_id': pickid,
'state':'waiting',
'prodlot_id':False,
'tracking_id':False,
'move_history_ids':[],
'date_planned': DateTime.strptime(move.date_planned, '%Y-%m-%d') + DateTime.RelativeDateTime(days=move.location_dest_id.chained_delay or 0),
'move_history_ids2':[]}
)
self.pool.get('stock.move').write(cr, uid, [move.id], {
'move_dest_id': new_id,
'move_history_ids': [(4, new_id)]
})
wf_service = netsvc.LocalService("workflow")
wf_service.trg_validate(uid, 'stock.picking', pickid, 'button_confirm', cr)
self.pool.get('stock.move').action_confirm(cr,uid, todo, context)
return True
def test_auto_picking(self, cr, uid, ids):
@ -762,7 +717,23 @@ class stock_move(osv.osv):
'price_unit': fields.float('Unit Price',
digits=(16, int(config['price_accuracy']))),
}
def _default_location_destination(self, cr, uid, context={}):
if context.get('move_line', []):
return context['move_line'][0][2]['location_dest_id']
if context.get('address_out_id', False):
return self.pool.get('res.partner.address').browse(cr, uid, context['address_out_id'], context).partner_id.property_stock_customer.id
return False
def _default_location_source(self, cr, uid, context={}):
if context.get('move_line', []):
return context['move_line'][0][2]['location_id']
if context.get('address_in_id', False):
return self.pool.get('res.partner.address').browse(cr, uid, context['address_in_id'], context).partner_id.property_stock_supplier.id
return False
_defaults = {
'location_id': _default_location_source,
'location_dest_id': _default_location_destination,
'state': lambda *a: 'draft',
'priority': lambda *a: '1',
'product_qty': lambda *a: 1.0,
@ -795,9 +766,55 @@ class stock_move(osv.osv):
result['location_dest_id'] = loc_dest_id
return {'value':result}
def action_confirm(self, cr, uid, ids, context={}):
def _chain_compute(self, cr, uid, moves, context={}):
result = {}
for m in moves:
dest = self.pool.get('stock.location').chained_location_get(cr, uid, m.location_dest_id, m.picking_id and m.picking_id.address_id and m.picking_id.address_id.partner_id, m.product_id, context)
if dest:
if dest[1]=='transparent':
self.write(cr, uid, [m.id], {
'date_planned': (DateTime.strptime(m.date_planned, '%Y-%m-%d') + \
DateTime.RelativeDateTime(days=dest[2] or 0)).strftime('%Y-%m-%d'),
'location_dest_id': dest[0].id})
else:
result.setdefault(m.picking_id, [])
result[m.picking_id].append( (m, dest) )
return result
def action_confirm(self, cr, uid, moves, context={}):
ids = map(lambda m: m.id, moves)
self.write(cr, uid, ids, {'state':'confirmed'})
return True
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])
pickid = self.pool.get('stock.picking').create(cr, uid, {
'name': picking.name,
'origin': str(picking.origin or ''),
'type': ptype,
'note': picking.note,
'move_type': picking.move_type,
'auto_picking': todo[0][1][1]=='auto',
'address_id': picking.address_id.id,
'invoice_state': 'none'
})
for move,(loc,auto,delay) in todo:
# Is it smart to copy ? May be it's better to recreate ?
new_id = self.pool.get('stock.move').copy(cr, uid, move.id, {
'location_id': move.location_dest_id.id,
'location_dest_id': loc.id,
'date_moved': time.strftime('%Y-%m-%d'),
'picking_id': pickid,
'state':'waiting',
'move_history_ids':[],
'date_planned': (DateTime.strptime(move.date_planned, '%Y-%m-%d') + DateTime.RelativeDateTime(days=delay or 0)).strftime('%Y-%m-%d'),
'move_history_ids2':[]}
)
self.pool.get('stock.move').write(cr, uid, [move.id], {
'move_dest_id': new_id,
'move_history_ids': [(4, new_id)]
})
wf_service = netsvc.LocalService("workflow")
wf_service.trg_validate(uid, 'stock.picking', pickid, 'button_confirm', cr)
return []
def action_assign(self, cr, uid, ids, *args):
todo = []
@ -1092,63 +1109,6 @@ class stock_warehouse(osv.osv):
stock_warehouse()
# Product
class product_product(osv.osv):
_inherit = "product.product"
#
# Utiliser browse pour limiter les queries !
#
def view_header_get(self, cr, user, view_id, view_type, context):
res = super(product_product, self).view_header_get(cr, user, view_id, view_type, context)
if res: return res
if (not context.get('location', False)):
return False
cr.execute('select name from stock_location where id=%d', (context['location'],))
j = cr.fetchone()[0]
if j:
return 'Products: '+j
return False
def _get_product_available_func(states, what):
def _product_available(self, cr, uid, ids, name, arg, context={}):
if context.get('shop', False):
cr.execute('select warehouse_id from sale_shop where id=%d', (int(context['shop']),))
res2 = cr.fetchone()
if res2:
context['warehouse'] = res2[0]
if context.get('warehouse', False):
cr.execute('select lot_stock_id from stock_warehouse where id=%d', (int(context['warehouse']),))
res2 = cr.fetchone()
if res2:
context['location'] = res2[0]
if context.get('location', False):
location_ids = [context['location']]
else:
# get the list of ids of the stock location of all warehouses
cr.execute("select lot_stock_id from stock_warehouse")
location_ids = [id for (id,) in cr.fetchall()]
# build the list of ids of children of the location given by id
location_ids = self.pool.get('stock.location').search(cr, uid, [('location_id', 'child_of', location_ids)])
res = self.pool.get('stock.location')._product_get_multi_location(cr, uid, location_ids, ids, context, states, what)
for id in ids:
res.setdefault(id, 0.0)
return res
return _product_available
_product_qty_available = _get_product_available_func(('done',), ('in', 'out'))
_product_virtual_available = _get_product_available_func(('confirmed','waiting','assigned','done'), ('in', 'out'))
_product_outgoing_qty = _get_product_available_func(('confirmed','waiting','assigned'), ('out',))
_product_incoming_qty = _get_product_available_func(('confirmed','waiting','assigned'), ('in',))
_columns = {
'qty_available': fields.function(_product_qty_available, method=True, type='float', string='Real Stock'),
'virtual_available': fields.function(_product_virtual_available, method=True, type='float', string='Virtual Stock'),
'incoming_qty': fields.function(_product_incoming_qty, method=True, type='float', string='Incoming'),
'outgoing_qty': fields.function(_product_outgoing_qty, method=True, type='float', string='Outgoing'),
}
product_product()
# Move wizard :
# get confirm or assign stock move lines of partner and put in current picking.
class stock_picking_move_wizard(osv.osv_memory):

View File

@ -1,42 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<terp>
<data noupdate="1">
<data>
<!--
Sequences for packings
-->
Sequences for packings
-->
<record id="seq_type_picking_out" model="ir.sequence.type">
<field name="name">Packing Out</field>
<field name="code">stock.picking.out</field>
<record id="seq_type_picking" model="ir.sequence.type">
<field name="name">Packing</field>
<field name="code">stock.picking</field>
</record>
<record id="seq_type_picking_in" model="ir.sequence.type">
<field name="name">Packing In</field>
<field name="code">stock.picking.in</field>
</record>
<record id="seq_type_picking_internal" model="ir.sequence.type">
<field name="name">Packing Internal</field>
<field name="code">stock.picking.internal</field>
</record>
<record id="seq_picking_out" model="ir.sequence">
<field name="name">Packing Out</field>
<field name="code">stock.picking.out</field>
<field name="prefix">OUT:</field>
</record>
<record id="seq_picking_in" model="ir.sequence">
<field name="name">Packing In</field>
<field name="code">stock.picking.in</field>
<field name="prefix">IN:</field>
</record>
<record id="seq_picking_internal" model="ir.sequence">
<field name="name">Packing Internal</field>
<field name="code">stock.picking.internal</field>
<field name="prefix">INTERNAL:</field>
<record id="seq_picking" model="ir.sequence">
<field name="name">Packing</field>
<field name="code">stock.picking</field>
<field name="prefix">PACK</field>
</record>
<!--
Sequences from tracking numbers
-->
Sequences from tracking numbers
-->
<record id="sequence_type_serial" model="ir.sequence.type">
<field name="name">Stock Production Lots</field>
<field name="code">stock.lot.serial</field>
@ -44,7 +26,7 @@
<record id="sequence_production_lots" model="ir.sequence">
<field name="name">Stock Production Lots</field>
<field name="code">stock.lot.serial</field>
<field name="prefix">1234567890</field>
<field name="prefix"></field>
<field name="padding">7</field>
<field name="number_next">1</field>
<field name="number_increment">1</field>
@ -59,10 +41,10 @@
<record id="sequence_tracking" model="ir.sequence">
<field name="name">Stock Tracking Lots</field>
<field name="code">stock.lot.tracking</field>
<field name="prefix">1234567890</field>
<field name="prefix"></field>
<field name="padding">7</field>
<field name="number_next">1</field>
<field name="number_increment">1</field>
</record>
</data>
</terp>
</terp>

View File

@ -28,8 +28,8 @@
<form string="Stock Inventory Lines">
<field colspan="4" domain="[('usage','=','internal')]" name="location_id" select="1"/>
<field context="location=location_id,uom=product_uom" name="product_id" on_change="on_change_product_id(location_id,product_id,product_uom)" select="1"/>
<field name="product_uom"/>
<field name="product_qty"/>
<field name="product_uom"/>
</form>
</field>
</record>
@ -313,7 +313,7 @@
<field name="usage" select="1"/>
<field name="account_id" select="1"/>
<field name="location_id"/>
<field name="address_id"/>
<field name="address_id" context="{'context_display':'partner'}"/>
<group col="2" colspan="2">
<separator string="Chained Locations" colspan="2"/>
<field name="chained_location_type"/>
@ -391,7 +391,7 @@
<field name="lot_stock_id"/>
<field name="lot_output_id"/>
<newline/>
<field name="partner_address_id"/>
<field name="partner_address_id" context="{'context_display':'partner'}"/>
</form>
</field>
</record>
@ -405,7 +405,7 @@
<field name="lot_input_id"/>
<field name="lot_stock_id"/>
<field name="lot_output_id"/>
<field name="partner_address_id"/>
<field name="partner_address_id" context="{'context_display':'partner'}"/>
</tree>
</field>
</record>
@ -427,7 +427,7 @@
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Move Lines">
<field name="address_id" invisible="True"/>
<field name="address_id" invisible="True" context="{'context_display':'partner'}"/>
<field name="picking_id" invisible="True"/>
<field domain="[('picking_id','&lt;&gt;',picking_id),('state','in',['confirmed','assigned'])]" name="move_ids" select="1"/><newline/>
<group colspan="4">
@ -461,7 +461,7 @@
<tree color="red:state=='cancel'" string="Packing list">
<field colspan="4" name="name" select="1"/>
<field name="date" select="1"/>
<field name="address_id" select="1"/>
<field name="address_id" select="1" context="{'context_display':'partner'}"/>
<field name="invoice_state" readonly="1"/>
<field name="origin" select="1"/>
<field name="state" readonly="1"/>
@ -476,37 +476,31 @@
<form string="Packing list">
<notebook>
<page string="General Information">
<field name="name" select="1"/>
<field name="address_id" select="2" context="{'context_display':'partner'}"/>
<field name="type" select="2" readonly="1"/>
<field name="name" select="1" readonly="1"/>
<field name="date" select="1"/>
<newline/>
<field name="type" select="2"/>
<field name="invoice_state" select="2"/>
<newline/>
<field name="location_id"/>
<field name="location_dest_id"/>
<field name="address_id" select="2"/>
<field colspan="4" name="move_lines" nolabel="1" widget="one2many_list">
<field colspan="4" name="move_lines" nolabel="1" widget="one2many_list" default_get="{'move_line':move_lines, 'address_out_id': address_id}">
<form string="Stock Moves">
<separator colspan="4" string="Move Information"/>
<field colspan="4" context="location=parent.location_id" name="product_id" on_change="onchange_product_id(product_id, parent.location_id, parent.location_dest_id)" select="1"/>
<field name="product_uom" select="1"/>
<field name="location_id" select="1" domain="[('usage','=','internal')]"/>
<field name="location_dest_id" select="1" domain="[('usage','=','internal')]"/>
<field colspan="4" context="location=location_id" name="product_id" on_change="onchange_product_id(product_id, location_id, location_dest_id)" select="1"/>
<field name="product_qty" select="1"/>
<field name="product_uom" select="1"/>
<field groups="product.group_uos" name="product_uos"/>
<field groups="product.group_uos" name="product_uos_qty"/>
<field colspan="4" name="name" select="1"/>
<field name="date"/>
<field colspan="4" invisible="1" name="name" select="1"/>
<field invisible="1" name="date"/>
<field name="date_planned"/>
<field name="priority"/>
<field name="location_id" select="1"/>
<field name="location_dest_id" select="1"/>
<field name="address_id" select="1"/>
<field name="product_packaging"/>
<field name="address_id" select="1" context="{'context_display':'partner'}"/>
<field groups="base.group_extended" name="product_packaging"/>
<field context="product_id=product_id" name="prodlot_id" select="1"/>
<field name="tracking_id" select="1"/>
<field groups="base.group_extended" name="tracking_id" select="1"/>
<newline/>
<label/>
<button name="%(track_line)d" string="Split in production lots" type="action"/>
<separator colspan="4" string="Move State"/>
<field name="state" select="1"/>
<group>
@ -556,7 +550,7 @@
<tree color="red:state=='cancel'" string="Packing list">
<field colspan="4" name="name" select="1"/>
<field name="date" select="1"/>
<field name="address_id" select="1"/>
<field name="address_id" select="1" context="{'context_display':'partner'}"/>
<field name="origin" select="1"/>
<field name="state" readonly="1"/>
</tree>
@ -568,26 +562,25 @@
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Packing list">
<field name="name" select="1"/>
<field name="date" select="1"/>
<newline/>
<field name="address_id" select="2"/>
<field name="address_id" select="2" context="{'context_display':'partner'}"/>
<field name="origin" select="2"/>
<field name="name" select="1" readonly="1"/>
<field name="date" select="1" readonly="1"/>
<notebook colspan="4">
<page string="General Information">
<field colspan="4" name="move_lines" nolabel="1" widget="one2many_list">
<field colspan="4" name="move_lines" nolabel="1" widget="one2many_list" default_get="{'move_line':move_lines, 'address_out_id': address_id}">
<form string="Stock Moves">
<separator colspan="4" string="Move Information"/>
<field colspan="4" context="location=parent.location_id" name="product_id" on_change="onchange_product_id(product_id, parent.location_id, parent.location_dest_id)" select="1"/>
<field name="product_uom" select="1"/>
<field name="location_id" select="1" domain="[('usage','=','internal')]"/>
<field name="location_dest_id" select="1" domain="[('usage','&lt;&gt;','view')]"/>
<field colspan="4" context="location=location_id" name="product_id" on_change="onchange_product_id(product_id, location_id, location_dest_id)" select="1"/>
<field name="product_qty" select="1"/>
<field name="product_uom" select="1"/>
<field groups="product.group_uos" name="product_uos"/>
<field groups="product.group_uos" name="product_uos_qty"/>
<field colspan="4" invisible="1" name="name" select="1"/>
<field invisible="1" name="date"/>
<field name="date_planned"/>
<field name="location_id" select="1"/>
<field name="location_dest_id" select="1"/>
<field groups="base.group_extended" name="product_packaging"/>
<field context="product_id=product_id" name="prodlot_id" select="1"/>
<field groups="base.group_extended" name="tracking_id" select="1"/>
@ -710,7 +703,7 @@
<tree color="red:state=='cancel'" string="Packing list">
<field colspan="4" name="name" select="1"/>
<field name="date" select="1"/>
<field name="address_id" select="1"/>
<field name="address_id" select="1" context="{'context_display':'partner'}"/>
<field name="invoice_state" readonly="1"/>
<field name="origin" select="1"/>
<field name="state" readonly="1"/>
@ -723,26 +716,25 @@
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Packing list">
<field name="name" select="1"/>
<field name="date" select="1"/>
<newline/>
<field name="address_id" select="2"/>
<field name="address_id" select="2" context="{'context_display':'partner'}"/>
<field name="date" select="1" readonly="1"/>
<field name="origin" select="2"/>
<field name="name" select="1" readonly="1"/>
<notebook colspan="4">
<page string="General Information">
<field colspan="4" name="move_lines" nolabel="1" widget="one2many_list">
<field colspan="4" name="move_lines" nolabel="1" widget="one2many_list" default_get="{'move_line':move_lines, 'address_out_id': address_id}">
<form string="Stock Moves">
<separator colspan="4" string="Move Information"/>
<field colspan="4" context="location=parent.location_id" name="product_id" on_change="onchange_product_id(product_id, parent.location_id, parent.location_dest_id)" select="1"/>
<field name="product_uom" select="1"/>
<field name="location_id" select="1" domain="[('usage','=','internal')]"/>
<field name="location_dest_id" select="1" domain="[('usage','&lt;&gt;','view')]"/>
<field colspan="4" context="location=location_id" name="product_id" on_change="onchange_product_id(product_id, location_id, location_dest_id)" select="1"/>
<field name="product_qty" select="1"/>
<field name="product_uom" select="1"/>
<field groups="product.group_uos" name="product_uos"/>
<field groups="product.group_uos" name="product_uos_qty"/>
<field colspan="4" invisible="1" name="name" select="1"/>
<field invisible="1" name="date"/>
<field name="date_planned"/>
<field name="location_id" select="1"/>
<field name="location_dest_id" select="1"/>
<field groups="base.group_extended" name="product_packaging"/>
<field context="product_id=product_id" name="prodlot_id" select="1"/>
<field groups="base.group_extended" name="tracking_id" select="1"/>
@ -862,7 +854,7 @@
<tree color="red:state=='cancel'" string="Packing list">
<field colspan="4" name="name" select="1"/>
<field name="date" select="1"/>
<field name="address_id" select="1"/>
<field name="address_id" select="1" context="{'context_display':'partner'}"/>
<field name="invoice_state" readonly="1"/>
<field name="origin" select="1"/>
<field name="state" readonly="1"/>
@ -875,15 +867,13 @@
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Input Packing List">
<field name="address_id" on_change="onchange_partner_in(address_id)" select="2"/>
<field name="name" readonly="1" select="1"/>
<field name="location_id"/>
<field domain="[('usage','=','internal')]" name="location_dest_id"/>
<field name="invoice_state" select="2" string="Supplier Invoice Control"/>
<field name="address_id" on_change="onchange_partner_in(address_id)" select="2" context="{'context_display':'partner'}"/>
<field name="origin" select="2"/>
<field name="invoice_state" select="2" string="Invoice Control"/>
<field name="name" readonly="1" select="1"/>
<notebook colspan="4">
<page string="General Information">
<field colspan="4" name="move_lines" nolabel="1" widget="one2many_list">
<field colspan="4" name="move_lines" nolabel="1" widget="one2many_list" default_get="{'move_line':move_lines, 'address_in_id': address_id}">
<tree string="Stock Moves">
<field name="product_id"/>
<field name="product_qty"/>
@ -894,17 +884,17 @@
</tree>
<form string="Stock Moves">
<separator colspan="4" string="Move Information"/>
<field colspan="4" context="location=parent.location_id" name="product_id" on_change="onchange_product_id(product_id, parent.location_id, parent.location_dest_id)" select="1"/>
<field name="product_uom" select="1"/>
<field name="location_id" select="1" domain="[('usage','&lt;&gt;','view')]"/>
<field domain="[('usage','=','internal')]" name="location_dest_id" select="1"/>
<field colspan="4" context="location=location_id" name="product_id" on_change="onchange_product_id(product_id, location_id, location_dest_id)" select="1"/>
<field name="product_qty" select="1"/>
<field name="product_uom" select="1"/>
<field groups="product.group_uos" name="product_uos"/>
<field groups="product.group_uos" name="product_uos_qty"/>
<field colspan="4" invisible="1" name="name" select="1"/>
<field groups="base.group_extended" name="date"/>
<field groups="base.group_extended" name="date_planned"/>
<newline/>
<field name="location_id" select="1"/>
<field domain="[('usage','=','internal')]" name="location_dest_id" select="1"/>
<newline/>
<field groups="base.group_extended" name="product_packaging"/>
<newline/>
@ -1099,16 +1089,16 @@
<field name="arch" type="xml">
<form string="Stock Moves">
<separator colspan="4" string="Move Information"/>
<field name="location_id" select="1"/>
<field name="location_dest_id" select="1"/>
<field colspan="4" name="product_id" select="1"/>
<field name="product_qty" select="1"/>
<field name="product_uom" select="1"/>
<field colspan="4" name="name" select="1"/>
<field name="date"/>
<field name="date_planned"/>
<field colspan="4" name="product_id" select="1"/>
<field name="product_uom" select="1"/>
<field name="product_qty" select="1"/>
<field name="location_id" select="1"/>
<field name="location_dest_id" select="1"/>
<field name="priority"/>
<field name="address_id" select="1"/>
<field name="address_id" select="1" context="{'context_display':'partner'}"/>
<newline/>
<field context="product_id=product_id" name="prodlot_id" select="1"/>
<field name="tracking_id" select="1"/>

View File

@ -0,0 +1,31 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2006 TINY SPRL. (http://tiny.be) All Rights Reserved.
# Fabien Pinckaers <fp@tiny.Be>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
import stock

View File

@ -0,0 +1,25 @@
{
"name" : "Stock Location Paths",
"version" : "1.0",
"author" : "Tiny",
"depends" : [ "stock"],
"category" : "Generic Modules/Inventory Control",
"description":"""
Manages product's path in locations.
This module may be usefull for different purposes:
* Manages the product in his whole manufacturing chain
* Manages different default locations by products
* Define paths within the warehouse to route products based on operations:
- Quality Control
- After Sales Services
- Supplier Return
* Manage products to be rent.
""",
"init_xml" : [],
"demo_xml" : [],
"update_xml" : ["stock_view.xml"],
"active": False,
"installable": True
}

View File

@ -0,0 +1,77 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2006 TINY SPRL. (http://tiny.be) All Rights Reserved.
# Fabien Pinckaers <fp@tiny.Be>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
from osv import fields,osv
import tools
import ir
import pooler
class stock_location_path(osv.osv):
_name = "stock.location.path"
_columns = {
'name': fields.char('Operation', size=64),
'product_id' : fields.many2one('product.product', 'Products', ondelete='cascade', select=1),
'location_from_id' : fields.many2one('stock.location', 'Source Location', ondelete='cascade', select=1),
'location_dest_id' : fields.many2one('stock.location', 'Destination Location', ondelete='cascade', select=1),
'delay': fields.integer('Delay (days)', help="Number of days to do this transition"),
'auto': fields.selection(
[('auto','Automatic Move'), ('manual','Manual Operation'),('transparent','Automatic No Step Added')],
'Automatic Move',
required=True, select=1,
help="This is used to define paths the product has to follow within the location tree.\n" \
"The 'Automatic Move' value will create a stock move after the current one that will be "\
"validated automatically. With 'Manual Operation', the stock move has to be validated "\
"by a worker. With 'Automatic No Step Added', the location is replaced in the original move."
),
}
_defaults = {
'auto': lambda *arg: 'auto',
'delay': lambda *arg: 1
}
stock_location_path()
class product_product(osv.osv):
_inherit = 'product.product'
_columns = {
'path_ids': fields.one2many('stock.location.path', 'product_id',
'Location Paths',
help="These rules set the right path of the product in the "\
"whole location tree.")
}
product_product()
class stock_location(osv.osv):
_inherit = 'stock.location'
def chained_location_get(self, cr, uid, location, partner=None, product=None, context={}):
if product:
for path in product.path_ids:
if path.location_from_id.id == location.id:
return path.location_dest_id, path.auto, path.delay
return super(stock_location, self).chained_location_get(cr, uid, location, partner, product, contex)
stock_location()

View File

@ -0,0 +1,58 @@
<?xml version="1.0"?>
<terp>
<data>
<record id="stock_location_path_tree" model="ir.ui.view">
<field name="name">stock.location.path.tree</field>
<field name="model">stock.location.path</field>
<field name="type">tree</field>
<field name="arch" type="xml">
<tree string="Location Paths">
<field name="product_id"/>
<field name="location_from_id"/>
<field name="location_dest_id"/>
<field name="name"/>
</tree>
</field>
</record>
<record id="stock_location_path_form" model="ir.ui.view">
<field name="name">stock.location.path.form</field>
<field name="model">stock.location.path</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Location Paths">
<field name="name"/>
<newline/>
<field name="product_id"/>
<newline/>
<field name="location_from_id"/>
<field name="location_dest_id"/>
<field name="auto"/>
<field name="delay"/>
</form>
</field>
</record>
<record id="product_normal_form_inherit_location" model="ir.ui.view">
<field name="name">product.product.form</field>
<field name="model">product.product</field>
<field name="type">form</field>
<field name="inherit_id" ref="product.product_normal_form_view"/>
<field name="arch" type="xml">
<page string="Procurement &amp; Locations">
<field name="path_ids" editable="bottom" colspan="4" nolabel="1">
<tree string="Location Paths" editable="bottom">
<field name="location_from_id"/>
<field name="location_dest_id"/>
<field name="auto"/>
<field name="delay"/>
<field name="name"/>
</tree>
</field>
</page>
</field>
</record>
</data>
</terp>