<notebook>
<page string="Line">
<field name="product_id" on_change="product_id_change(product_id, uos_id, quantity, name, parent.type, parent.partner_id)" select="1"/>
- <field name="uos_id"/>
+ <field name="uos_id"
+ context="product_id=product_id"
+ on_change="product_id_change(product_id, uos_id, quantity, name, parent.type, parent.partner_id)"
+ />
<field name="quantity" select="1"/>
<field name="price_unit" select="1"/>
<field name="discount"/>
if type in ('in_invoice', 'in_refund'):
return {'domain':{'product_uom':[]}}
else:
- return {'value': {'price_unit': 0.0}, 'domain':{'product_uom':[]}}
+ return {'value': {'price_unit': 0.0 }, 'domain':{'uos_id': []}}
lang=False
context.update({'lang': lang})
res = self.pool.get('product.product').browse(cr, uid, product, context=context)
result['account_id'] = a
domain = {}
- result['uos_id'] = uom or res.uom_id.id or False
+ result['uos_id'] = res.uom_id.id or uom or False
if result['uos_id']:
res2 = res.uom_id.category_id.id
if res2 :
domain = {'uos_id':[('category_id','=',res2 )]}
+ result
return {'value':result, 'domain':domain}
def move_line_get(self, cr, uid, invoice_id, context={}):
_description = 'Analytic Accounts'
def _credit_calc(self, cr, uid, ids, name, arg, context={}):
+ if 'date_start' in context and context['date_start']:
+ date_start = context['date_start']
+ else:
+ date_start = time.strftime('%Y-01-01')
+ if 'date_stop' in context and context['date_stop']:
+ date_stop = context['date_stop']
+ else:
+ date_stop = time.strftime('%Y-%m-%d')
acc_set = ",".join(map(str, ids))
- cr.execute("SELECT a.id, COALESCE(SUM(l.amount),0) FROM account_analytic_account a LEFT JOIN account_analytic_line l ON (a.id=l.account_id) WHERE l.amount<0 and a.id IN (%s) GROUP BY a.id" % acc_set)
+ cr.execute("SELECT a.id, COALESCE(SUM(l.amount),0) FROM account_analytic_account a LEFT JOIN account_analytic_line l ON (a.id=l.account_id) WHERE l.amount<0 and a.id IN (%s) AND (l.date BETWEEN '%s' and '%s') GROUP BY a.id" % (acc_set,date_start,date_stop))
r= dict(cr.fetchall())
for i in ids:
r.setdefault(i,0.0)
return r
def _debit_calc(self, cr, uid, ids, name, arg, context={}):
-
+ if 'date_start' in context and context['date_start']:
+ date_start = context['date_start']
+ else:
+ date_start = time.strftime('%Y-01-01')
+ if 'date_stop' in context and context['date_stop']:
+ date_stop = context['date_stop']
+ else:
+ date_stop = time.strftime('%Y-%m-%d')
acc_set = ",".join(map(str, ids))
- cr.execute("SELECT a.id, COALESCE(SUM(l.amount),0) FROM account_analytic_account a LEFT JOIN account_analytic_line l ON (a.id=l.account_id) WHERE l.amount>0 and a.id IN (%s) GROUP BY a.id" % acc_set)
+ cr.execute("SELECT a.id, COALESCE(SUM(l.amount),0) FROM account_analytic_account a LEFT JOIN account_analytic_line l ON (a.id=l.account_id) WHERE l.amount>0 and a.id IN (%s) AND (l.date BETWEEN '%s' and '%s') GROUP BY a.id" % (acc_set,date_start,date_stop))
r= dict(cr.fetchall())
for i in ids:
r.setdefault(i,0.0)
def _balance_calc(self, cr, uid, ids, name, arg, context={}):
+ if 'date_start' in context and context['date_start']:
+ date_start = context['date_start']
+ else:
+ date_start = time.strftime('%Y-01-01')
+ if 'date_stop' in context and context['date_stop']:
+ date_stop = context['date_stop']
+ else:
+ date_stop = time.strftime('%Y-%m-%d')
ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)])
acc_set = ",".join(map(str, ids2))
- cr.execute("SELECT a.id, COALESCE(SUM(l.amount),0) FROM account_analytic_account a LEFT JOIN account_analytic_line l ON (a.id=l.account_id) WHERE a.id IN (%s) GROUP BY a.id" % acc_set)
+ cr.execute("SELECT a.id, COALESCE(SUM(l.amount),0) FROM account_analytic_account a LEFT JOIN account_analytic_line l ON (a.id=l.account_id) WHERE a.id IN (%s) AND (l.date BETWEEN '%s' and '%s') GROUP BY a.id" % (acc_set,date_start,date_stop))
+# cr.execute("select a.id, COALESCE(SUM(l.amount),0) from account_analytic_account a LEFT JOIN account_analytic_line l ON (a.id=l.account_id) where (l.date BETWEEN '2008-10-01' AND '2008-10-03') GROUP BY a.id")
res = {}
for account_id, sum in cr.fetchall():
res[account_id] = sum
return dict([(i, res[i]) for i in ids ])
def _quantity_calc(self, cr, uid, ids, name, arg, context={}):
- #XXX must convert into one uom
+# XXX must convert into one uom
+ if 'date_start' in context and context['date_start']:
+ date_start = context['date_start']
+ else:
+ date_start = time.strftime('%Y-01-01')
+ if 'date_stop' in context and context['date_stop']:
+ date_stop = context['date_stop']
+ else:
+ date_stop = time.strftime('%Y-%m-%d')
ids2 = self.search(cr, uid, [('parent_id', 'child_of', ids)])
acc_set = ",".join(map(str, ids2))
- cr.execute('SELECT a.id, COALESCE(SUM(l.unit_amount), 0) \
+ cr.execute("SELECT a.id, COALESCE(SUM(l.unit_amount), 0) \
FROM account_analytic_account a \
LEFT JOIN account_analytic_line l ON (a.id = l.account_id) \
- WHERE a.id IN ('+acc_set+') GROUP BY a.id')
+ WHERE a.id IN (%s) AND (l.date BETWEEN '%s' and '%s') GROUP BY a.id" % (acc_set,date_start,date_stop))
res = {}
for account_id, sum in cr.fetchall():
res[account_id] = sum
if child <> id:
res.setdefault(id, 0.0)
res[id] += res.get(child, 0.0)
+ if not id in res:
+ res.setdefault(id, 0.0)
return dict([(i, res[i]) for i in ids])
def name_get(self, cr, uid, ids, context={}):
<field name="view_mode">form,graph</field>
<field name="view_id" ref="view_account_analytic_account_tree"/>
</record>
+
<menuitem
action="action_account_analytic_account_tree2"
id="account_analytic_def_chart"
parent="account.account_analytic_def_account"/>
+
+ <!--
+ OVERRIDDEN BY NEW WIZARD
<menuitem action="action_account_analytic_account_tree2" id="account_analytic_chart" parent="account.menu_finance_charts"/>
+ -->
+ <wizard id="action_analytic_account_chart" menu="False" model="account.analytic.account" name="account.analytic.line1" string="Analytic Chart of Accounts"/>
+
+ <menuitem type="wizard" icon="STOCK_INDENT" action="action_analytic_account_chart" id="account_analytic_chart" parent="account.menu_finance_charts" name="Analytic Account lines"/>
+
<record id="analytic_account_form" model="ir.actions.act_window">
<field name="name">New Analytic Account</field>
<field name="res_model">account.analytic.account</field>
import wizard_account_analytic_cost_ledger_for_journal_report
import wizard_account_analytic_analytic_check
import wizard_account_analytic_line
+import wizard_account_analytic_line1
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
--- /dev/null
+# -*- 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.
+#
+##############################################################################
+
+import wizard
+import pooler
+import time
+
+def _action_open_window(self, cr, uid, data, context):
+ domain=[]
+ cr.execute('select id,name from ir_ui_view where name=%s and type=%s', ('account.analytic.account.tree', 'tree'))
+ view_res = cr.fetchone()
+ return {
+ 'name': 'Analytic Entries',
+ 'view_type': 'tree',
+ "view_mode": 'tree,form',
+ 'res_model': 'account.analytic.account',
+ 'type': 'ir.actions.act_window',
+ 'view_id' : view_res,
+ 'domain' :[('parent_id','=',False)],
+ 'context':{'date_start':data['form']['from_date'],'date_stop':data['form']['to_date']},
+ }
+
+
+class account_analytic_line1(wizard.interface):
+ form1 = '''<?xml version="1.0"?>
+ <form string="View Account Analytic Lines">
+ <separator string="Account Analytic Lines Analysis" colspan="4"/>
+ <field name="from_date"/>
+ <newline/>
+ <field name="to_date"/>
+ <newline/>
+ <label string=""/>
+ <label string="(Keep empty to open the current situation)" align="0.0" colspan="3"/>
+ </form>'''
+ form1_fields = {
+ 'from_date': {
+ 'string': 'From',
+ 'type': 'date',
+ },
+ 'to_date': {
+ 'string': 'To',
+ 'type': 'date',
+ },
+ }
+
+ states = {
+ 'init': {
+ 'actions': [],
+ 'result': {'type': 'form', 'arch':form1, 'fields':form1_fields, 'state': [('end', 'Cancel','gtk-cancel'),('open', 'Open Entries','gtk-ok')]}
+ },
+ 'open': {
+ 'actions': [],
+ 'result': {'type': 'action', 'action': _action_open_window, 'state':'end'}
+ }
+ }
+account_analytic_line1('account.analytic.line1')
'close_move': lambda *a: 0,
'procure_method': lambda *a: 'make_to_order',
}
+
+ def unlink(self, cr, uid, ids):
+ procurements = self.read(cr, uid, ids, ['state'])
+ unlink_ids = []
+ for s in procurements:
+ if s['state'] in ['draft','cancel']:
+ unlink_ids.append(s['id'])
+ else:
+ raise osv.except_osv(_('Invalid action !'), _('Cannot delete Procurement Order(s) which are in %s State!' % s['state']))
+ osv.osv.unlink(self, cr, uid, unlink_ids)
+ return True
+
def check_product(self, cr, uid, ids):
for procurement in self.browse(cr, uid, ids):
if procurement.product_id.type in ('product', 'consu'):
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>
-<terp>
- <data>
- <report id="report_processus"
- model="processus.processus"
- name="processus.processus.print"
- string="Print Processus"/>
- </data>
-</terp>
-
+++ /dev/null
-# -*- encoding: utf-8 -*-
-##############################################################################
-#
-# Copyright (c) 2005-TODAY TINY SPRL. (http://tiny.be) All Rights Reserved.
-#
-# 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 report_processus
-
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
-
+++ /dev/null
-# -*- encoding: utf-8 -*-
-import Image
-import ImageDraw
-import ImageFont
-import math
-
-import addons
-
-ROUNDED = 30
-BGCOLOR = (228,233,237)
-TITLECOLOR = (150,70,70)
-FONT = addons.get_module_resource('processus', 'report/sb.ttf')
-BOXSIZE = (160,120)
-
-size = 800,600
-
-class draw(object):
- def _rounding_box(self, x, y, width, height, title=None, bgcolor=BGCOLOR):
- d = ImageDraw.Draw(self.img)
- DR = ROUNDED/2
- d.polygon( (x+DR,y,x+width-DR,y,x+width,y+DR,x+width,y+height-DR,x+width-DR,y+height,x+DR,y+height,x,y+height-DR,x,y+DR), fill=bgcolor)
- d.pieslice((x,y,x+ROUNDED,y+ROUNDED),180,270,fill=bgcolor)
- d.pieslice((x+width-ROUNDED,y,x+width,y+ROUNDED),270,0,fill=title and TITLECOLOR or bgcolor)
- d.pieslice((x+width-ROUNDED,y+height-ROUNDED,x+width,y+height),0,90,fill=bgcolor)
- d.pieslice((x,y+height-ROUNDED,x+ROUNDED,y+height),90,180,fill=bgcolor)
- if title:
- d.polygon( (x+width/5, y, x+width-DR, y, x+width, y+DR, x+width, y+20, x+width/5+15, y+20), fill=TITLECOLOR)
- self.draw_text(x+width/5+13, y+4, 10, title, (255,255,255), width*4/5-14)
-
- def node(self, x, y, width, height, title=None, start_color=BGCOLOR):
- self._rounding_box(x,y,BOXSIZE[0]-16,BOXSIZE[1], bgcolor=start_color)
- self._rounding_box(x+12,y,BOXSIZE[0]-16,BOXSIZE[1], bgcolor=(255,255,255))
- self._rounding_box(x+16,y,BOXSIZE[0]-16,BOXSIZE[1], title)
-
- def angle(self, arrow):
- if not arrow[1]-arrow[3]:
- angle = 180
- else:
- angle = math.atan(-(arrow[2]-arrow[0]) / (arrow[1]-arrow[3])) * 180 / math.pi
- angle = 270 - angle
- if arrow[3]<arrow[1]:
- angle = 180 + angle
- return int(angle)
-
- def arrow(self, arrow):
- d = ImageDraw.Draw(self.img)
- d.line(arrow, width=1, fill=(0,0,0))
- angle = self.angle(arrow)
- d.pieslice((arrow[2]-14,arrow[3]-14,arrow[2]+14,arrow[3]+14),angle-18,angle+18,fill=(0,0,0))
-
- def draw_text(self, x, y, size, title, color=(155,255,255), maxlength=None, font_name=FONT, center=False):
- d = ImageDraw.Draw(self.img)
- font = ImageFont.truetype(font_name, size)
- d.setfont(font)
- size2 = d.textsize(title)
- if maxlength:
- fontsize = min(size, size * maxlength / size2[0])
- font = ImageFont.truetype(font_name, fontsize)
- d.setfont(font)
- size = d.textsize(title)
- if center:
- x = x-size[0]/2
- d.text( (x, y+(size2[1]-size[1])/2), title, color)
-
- def arrow_role(self, node_from, node_to, role='Hello'):
- d = ImageDraw.Draw(self.img)
- x = (node_from[0] + node_to[0]) /2
- y = (node_from[1] + node_to[1]) /2
- angle = self.angle(node_from+node_to) + 105
- d.pieslice((x-40,y-40,x+40,y+40),angle-5,angle+5,fill=(100,0,0))
- d.pieslice((x-6,y-6,x+6,y+6),angle-7,angle+7,fill=(255,255,255))
-
- print -180 + angle - 90
- x = x + math.cos(angle * math.pi / 180) * 40
- y = y + math.sin(angle * math.pi / 180) * 40
- a,b = x,y
- angle -= 120
-
- x = x + math.cos(angle * math.pi / 180) * 20
- y = y + math.sin(angle * math.pi / 180) * 20
-
- d.line((a,b,x,y), width=5, fill=(100,0,0))
- angle += 125
- d.pieslice((x-30,y-30,x+30,y+30),angle-7,angle+7,fill=(100,0,0))
-
- x = x + math.cos(angle * math.pi / 180) * 50
- y = y + math.sin(angle * math.pi / 180) * 50
- return (x,y)
-
- def picture(self, x, y, fname):
- img = Image.open(fname)
- img2 = img.convert('RGBA')
- self.img.paste(img2, (max(0,x-img.size[0]/2), max(0,y-img.size[1])), mask=img2)
-
- def __init__(self, img):
- self.img = img
-
-class graph(object):
- def __init__(self, img):
- self.draw = draw(img)
-
- def intersect_one(self, start, stop):
- if start[0] < stop[0]:
- x1 = start[0] + BOXSIZE[0]/2 + 3
- else:
- x1 = start[0] - BOXSIZE[0]/2 - 3
- if not start[0]-stop[0]:
- y1 = 99999999999999
- else:
- y1 = start[1] - (start[1] - stop[1]) * (start[0]-x1) / (start[0] - stop[0])
-
- if start[1] < stop[1]:
- y2 = start[1] + BOXSIZE[1]/2 + 3
- else:
- y2 = start[1] - BOXSIZE[1]/2 - 3
- if not start[1]-stop[1]:
- x2 = 99999999999999
- else:
- x2 = start[0] - (start[0] - stop[0]) * (start[1]-y2) / (start[1] - stop[1])
-
- if abs(start[0]-x1)+abs(start[1]-y1)<abs(start[0]-x2)+abs(y2-start[1]):
- return (x1,y1)
- return (x2,y2)
-
- def intersect(self, start, stop):
- s = self.intersect_one(start,stop)
- s2 = self.intersect_one(stop,start)
- return s+s2
-
- def node(self, x, y, data, start_color=False):
- self.draw.node(x,y,BOXSIZE[0], BOXSIZE[1], data.get('title','Unknown'), start_color and (255,125,125) or BGCOLOR)
- self.draw.picture(x+35, y+BOXSIZE[1]-5, addons.get_module_resource('processus', 'report/gtk-help.png'))
- if start_color:
- self.draw.picture(x+65, y+BOXSIZE[1]-5, addons.get_module_resource('processus', 'report/gtk-open.png'))
- self.draw.picture(x+95, y+BOXSIZE[1]-5, addons.get_module_resource('processus', 'report/gtk-print.png'))
- y = y+25
- menus = data.get('menu','').split('/')
- while menus:
- menu = menus.pop(0)
- if menu:
- if menus: menu=menu+' /'
- self.draw.draw_text(x+23, y, 10, menu, color=(0,0,0), maxlength=BOXSIZE[0] - 25,
- font_name=addons.get_module_resource('processus', 'report/ds.ttf'))
- y+=15
-
- def arrow_role(self, node_from, node_to, role='Hello'):
- start = (node_from[0]+BOXSIZE[0]/2, node_from[1]+BOXSIZE[1]/2)
- stop = (node_to[0]+BOXSIZE[0]/2, node_to[1]+BOXSIZE[1]/2)
- (x,y) = self.draw.arrow_role(start, stop, role)
- self.draw.picture(x, y-3, addons.get_module_resource('processus', 'report/role.png'))
- self.draw.draw_text(x,y-3, 12, 'Salesman', color=(0,0,0), center=True)
-
- def arrow(self, node_from, node_to):
- start = (node_from[0]+BOXSIZE[0]/2, node_from[1]+BOXSIZE[1]/2)
- stop = (node_to[0]+BOXSIZE[0]/2, node_to[1]+BOXSIZE[1]/2)
- arrow = self.intersect(start,stop)
- self.draw.arrow(arrow)
-
-if __name__=='__main__':
- img = Image.new('RGB',size,'#ffffff')
- g = graph(img)
- g.node(50,100,{'title':'SALE AZER ORDER', 'menu':'Sales Management/Sales Orders/My Sales Order/My Open Sales Order'}, start_color=(200,100,100))
- g.node(350,150,{'title':'SALE AZER AZE ORDER', 'menu':'Sales Management/Sales Orders/My Quotations'})
- g.arrow((50,100),(350,150))
-
- g.arrow_role((50,100),(350,150))
-
- #img.show()
- img.save('a.pdf')
-
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
-
+++ /dev/null
-# -*- encoding: utf-8 -*-
-##############################################################################
-#
-# Copyright (c) 2004-2008 TINY SPRL. (http://tiny.be) All Rights Reserved.
-#
-# $Id: print_instance.py 8595 2008-06-16 13:00:21Z stw $
-#
-# 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 time, os
-
-import netsvc
-import report,pooler,tools
-
-import processus_print
-import Image
-
-
-class report_graph_instance(object):
- def __init__(self, cr, uid, ids, data):
- current_object = 'sale.order'
- pool = pooler.get_pool(cr.dbname)
- for processus in pool.get('processus.processus').browse(cr, uid, ids):
- nodes = {}
- start = []
- transitions = {}
- for node in processus.node_ids:
- nodes[node.id] = node
- if node.flow_start:
- start.append(node.id)
- for tr in node.transition_out:
- transitions[tr.id] = tr
- g = tools.graph(nodes.keys(), map(lambda x: (x.node_from_id.id,x.node_to_id.id), transitions.values()))
- g.process(start)
- g.scale(250,250, 100, 10)
-
- img = Image.new('RGB',(1024,768),'#ffffff')
- g2 = processus_print.graph(img)
- positions = g.result
- for name,node in positions.items():
- start_color = (nodes[name].model_id.model==current_object)
- g2.node(node['y'],node['x'], {
- 'title': nodes[name].name,
- 'menu': nodes[name].menu_id.complete_name
- }, start_color=start_color)
- for name,transition in transitions.items():
- if transition.transition_ids:
- g2.arrow_role((positions[transition.node_from_id.id]['y'], positions[transition.node_from_id.id]['x']),
- (positions[transition.node_to_id.id]['y'], positions[transition.node_to_id.id]['x']))
- g2.arrow((positions[transition.node_from_id.id]['y'], positions[transition.node_from_id.id]['x']),
- (positions[transition.node_to_id.id]['y'], positions[transition.node_to_id.id]['x']))
- img.save('/tmp/a.pdf')
- self.result = file('/tmp/a.pdf').read()
- self.done = True
-
- def is_done(self):
- return self.done
-
- def get(self):
- if self.done:
- return self.result
- else:
- return None
-
-class report_graph(report.interface.report_int):
- def __init__(self, name, table):
- report.interface.report_int.__init__(self, name)
- self.table = table
-
- def result(self):
- if self.obj.is_done():
- return (True, self.obj.get(), 'pdf')
- else:
- return (False, False, False)
-
- def create(self, cr, uid, ids, data, context={}):
- self.obj = report_graph_instance(cr, uid, ids, data)
- return (self.obj.get(), 'pdf')
-
-report_graph('report.processus.processus.print', 'processus.processus')
-
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
-
help='Coefficient to convert UOM to UOS\n'
' uom = uos * coeff'),
'mes_type': fields.selection((('fixed', 'Fixed'), ('variable', 'Variable')), 'Measure Type', required=True),
+ 'track_production' : fields.boolean('Track Production Lots' , help="Force to use a Production Lot number during Production"),
+ 'track_incoming' : fields.boolean('Track Incomming Lots', help="Force to use a Production Lot number during Purchase"),
+ 'track_outgoing' : fields.boolean('Track Outging Lots', help="Force to use a Production Lot number during Sale"),
+ 'track_other' : fields.boolean('Track Lots', help="Force to use a Production Lot number during stock operations for traceability."),
'seller_delay': fields.function(_calc_seller_delay, method=True, type='integer', string='Supplier Lead Time', help="This is the average delay in days between the purchase order confirmation and the reception of goods for this product and for the default supplier. It is used by the scheduler to order requests based on reordering delays."),
'seller_ids': fields.one2many('product.supplierinfo', 'product_id', 'Partners'),
'loc_rack': fields.char('Rack', size=16),
<field groups="base.group_extended" name="product_manager" select="2"/>
</group>
- <group colspan="2" col="2" name="uom">
- <separator string="UOM" colspan="2"/>
+ <group colspan="2" col="2" name="lot">
+ <separator string="Lots" colspan="2"/>
+ <group colspan="2" col="2" name="uom">
<field name="uom_id" on_change="onchange_uom(uom_id,uom_po_id)"/>
<field name="uom_po_id"/>
+ </group>
+ <group colspan="1" col="2" name="tracking1">
+ <field name="track_production"/>
+ <field name="track_incoming"/>
+ </group>
+ <group colspan="1" col="2" name="tracking2">
+ <field name="track_outgoing"/>
+ <field name="track_other"/>
+ </group>
</group>
<group colspan="2" col="2" name="uos" groups="product.group_uos">
_name = "purchase.order"
_description = "Purchase order"
_order = "name desc"
-
+
+ def unlink(self, cr, uid, ids):
+ purchase_orders = self.read(cr, uid, ids, ['state'])
+ unlink_ids = []
+ for s in purchase_orders:
+ if s['state'] in ['draft','cancel']:
+ unlink_ids.append(s['id'])
+ else:
+ raise osv.except_osv(_('Invalid action !'), _('Cannot delete Purchase Order(s) which are in %s State!' % s['state']))
+ osv.osv.unlink(self, cr, uid, unlink_ids)
+ return True
+
def button_dummy(self, cr, uid, ids, context={}):
return True
_order = 'name desc'
# Form filling
+ def unlink(self, cr, uid, ids):
+ sale_orders = self.read(cr, uid, ids, ['state'])
+ unlink_ids = []
+ for s in sale_orders:
+ if s['state'] in ['draft','canceled']:
+ unlink_ids.append(s['id'])
+ else:
+ raise osv.except_osv(_('Invalid action !'), _('Cannot delete Sale Order(s) which are already confirmed !'))
+ osv.osv.unlink(self, cr, uid, unlink_ids)
+ return True
+
def onchange_shop_id(self, cr, uid, ids, shop_id):
v={}
if shop_id:
'order_policy': lambda *a: 'picking'
}
def set_default(self, cr, uid, ids, context=None):
- print context
for o in self.browse(cr, uid, ids, context=context):
- print o.picking_policy, o.order_policy
ir_values_obj = self.pool.get('ir.values')
ir_values_obj.set(cr,uid,'default',False,'picking_policy',['sale.order'],o.picking_policy)
ir_values_obj = self.pool.get('ir.values')
--- /dev/null
+# -*- 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.
+#
+##############################################################################
+
+import sale_delivery
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+
--- /dev/null
+# -*- 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.
+###############################################################################
+# -*- encoding: utf-8 -*-
+{
+ "name":"Sale Delivery Planning",
+ "version":"1.0",
+ "author":"Tiny",
+ "category":"Custom",
+ "depends":["sale"],
+ "demo_xml":[],
+ "update_xml":["sale_delivery_view.xml"],
+ "active": False,
+ "installable": True,
+}
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+
--- /dev/null
+# -*- 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.
+#
+##############################################################################
+
+import time
+from osv import fields,osv
+import netsvc
+
+class sale_delivery_line(osv.osv):
+ _name = 'sale.delivery.line'
+ def _check_product_qty(self, cr, uid, ids):
+ for delivery in self.browse(cr, uid, ids):
+ cr.execute('select sum(product_uom_qty) from sale_order_line where order_id = %d and product_id = %d',(delivery.order_id,delivery.product_id))
+ sale_product_qty = cr.fetchall()[0][0]
+ cr.execute('select sum(product_qty) from sale_delivery_line where order_id = %d and product_id = %d',(delivery.order_id,delivery.product_id))
+ product_qty = cr.fetchall()[0][0]
+ if sale_product_qty < product_qty:
+ return False
+ return True
+
+ def _check_products(self, cr, uid, ids):
+ for delivery in self.browse(cr, uid, ids):
+ cr.execute('select id from sale_order_line where order_id = %d and product_id = %d',(delivery.order_id,delivery.product_id))
+ if not len(cr.fetchall()):
+ return False
+ return True
+
+ _columns = {
+ 'product_id': fields.many2one('product.product', string='Product', required=True ),
+ 'product_qty': fields.float('Product Quantity', digits=(16,2), required=True),
+ 'product_uom' : fields.many2one('product.uom', 'Product UoM', required=True),
+ 'packaging_id' : fields.many2one('product.packaging', 'Packaging'),
+ 'date_planned': fields.datetime('Date Planned', select=True, required=True),
+ 'priority': fields.integer('Priority'),
+ 'note' : fields.text('Note'),
+ 'order_id': fields.many2one('sale.order', 'Order Ref', required=True, ondelete='cascade', select=True),
+ }
+ _constraints = [
+ (_check_products,
+ 'You have selected a product which is not in related Sale Order',
+ ['product_id']),
+ (_check_product_qty,
+ 'The quanitties plannified in Deliveries must be equals to or less then the quantities in the Sale Order lines',
+ ['product_qty'])]
+
+sale_delivery_line()
+
+
+class sale_order(osv.osv):
+ _inherit = 'sale.order'
+ _columns = {
+ 'delivery_line': fields.one2many('sale.delivery.line', 'order_id', 'Delivery Lines', readonly=True, states={'draft':[('readonly',False)]}),
+ }
+
+ def action_ship_create(self, cr, uid, ids, *args):
+# picking_id=False
+ picking = {}
+ company = self.pool.get('res.users').browse(cr, uid, uid).company_id
+ for order in self.browse(cr, uid, ids, context={}):
+ output_id = order.shop_id.warehouse_id.lot_output_id.id
+# picking_id = False
+# if not order.delivery_line:
+# return super(sale_order, self).action_ship_create(cr, uid, ids)
+ for line in order.delivery_line:
+ cr.execute('select id from sale_order_line where order_id = %d and product_id = %d',(ids[0],line.product_id.id))
+ sale_line_id = cr.fetchall()[0][0]
+ sale_line = self.pool.get('sale.order.line').browse(cr, uid, sale_line_id)
+ proc_id=False
+ date_planned = line.date_planned
+# date_planned = (date_planned - DateTime.RelativeDateTime(days=company.security_lead)).strftime('%Y-%m-%d %H:%M:%S')
+# if line.state == 'done':
+# continue
+ if line.product_id and line.product_id.product_tmpl_id.type in ('product', 'consu'):
+ location_id = order.shop_id.warehouse_id.lot_stock_id.id
+# if not picking_id:
+ if not date_planned in picking:
+ loc_dest_id = order.partner_id.property_stock_customer.id
+ picking_id = self.pool.get('stock.picking').create(cr, uid, {
+ 'origin': order.name,
+ 'type': 'out',
+ 'state': 'confirmed',
+ 'move_type': order.picking_policy,
+ 'sale_id': order.id,
+ 'address_id': order.partner_shipping_id.id,
+ 'note': order.note,
+ 'invoice_state': (order.order_policy=='picking' and '2binvoiced') or 'none',
+ })
+ picking[date_planned] = picking_id
+
+ else:
+ picking_id = picking[date_planned]
+
+ move_id = self.pool.get('stock.move').create(cr, uid, {
+ 'name': line.product_id.name[:64],
+ 'picking_id': picking_id,
+ 'product_id': line.product_id.id,
+ 'date_planned': date_planned,
+ 'product_qty': line.product_qty,
+ 'product_uom': line.product_uom.id,
+ 'product_uos_qty': line.product_qty,
+ 'product_uos': line.product_uom.id,
+ 'product_packaging' : line.packaging_id.id,
+ 'address_id' : order.partner_shipping_id.id,
+ 'location_id': location_id,
+ 'location_dest_id': output_id,
+ 'tracking_id': False,
+ 'state': 'waiting',
+ 'note': line.note,
+ })
+ proc_id = self.pool.get('mrp.procurement').create(cr, uid, {
+ 'name': order.name,
+ 'origin': order.name,
+ 'date_planned': date_planned,
+ 'product_id': line.product_id.id,
+ 'product_qty': line.product_qty,
+ 'product_uom': line.product_uom.id,
+ 'product_uos_qty': line.product_qty,
+ 'product_uos': line.product_uom.id,
+ 'location_id': order.shop_id.warehouse_id.lot_stock_id.id,
+ 'procure_method': sale_line.type,
+ 'move_id': move_id,
+ 'property_ids': [(6, 0, [x.id for x in sale_line.property_ids])],
+ })
+ wf_service = netsvc.LocalService("workflow")
+ wf_service.trg_validate(uid, 'mrp.procurement', proc_id, 'button_confirm', cr)
+# self.pool.get('sale.order.line').write(cr, uid, [line.id], {'procurement_id': proc_id})
+ elif line.product_id and line.product_id.product_tmpl_id.type=='service':
+ proc_id = self.pool.get('mrp.procurement').create(cr, uid, {
+ 'name': line.name,
+ 'origin': order.name,
+ 'date_planned': date_planned,
+ 'product_id': line.product_id.id,
+ 'product_qty': line.product_uom_qty,
+ 'product_uom': line.product_uom.id,
+ 'location_id': order.shop_id.warehouse_id.lot_stock_id.id,
+ 'procure_method': line.type,
+ 'property_ids': [(6, 0, [x.id for x in line.property_ids])],
+ })
+ wf_service = netsvc.LocalService("workflow")
+ wf_service.trg_validate(uid, 'mrp.procurement', proc_id, 'button_confirm', cr)
+# self.pool.get('sale.order.line').write(cr, uid, [line.id], {'procurement_id': proc_id})
+ else:
+ #
+ # No procurement because no product in the sale.order.line.
+ #
+ pass
+ wf_service = netsvc.LocalService("workflow")
+ wf_service.trg_validate(uid, 'stock.picking', picking['date_planned'], 'button_confirm', cr)
+
+ val = {}
+# if 'date_planned' in picking and picking['date_planned']:
+# print
+
+ if order.state=='shipping_except':
+ val['state'] = 'progress'
+ if (order.order_policy == 'manual') and order.invoice_ids:
+ val['state'] = 'manual'
+ self.write(cr, uid, [order.id], val)
+
+ return True
+sale_order()
+
+class sale_order_line(osv.osv):
+ _inherit = 'sale.order.line'
+
+ def _get_planned_deliveries(self, cr, uid, ids, field_name, arg, context):
+ res = {}
+ for val in self.browse(cr, uid, ids):
+ cr.execute('select sum(product_qty) from sale_delivery_line where order_id = %d and product_id = %d',(val.order_id,val.product_id))
+ product_qty = cr.fetchall()[0][0]
+ res[val.id] = product_qty
+ return res
+
+ _columns = {
+ 'deliveries': fields.function(_get_planned_deliveries, method=True, string='Planned Deliveries'),
+ }
+
+sale_order_line()
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+ <data>
+
+ <record id="view_sale_delivery_form" model="ir.ui.view">
+ <field name="name">sale.delivery.form.inherit</field>
+ <field name="model">sale.order</field>
+ <field name="type">form</field>
+ <field name="inherit_id" ref="sale.view_order_form"/>
+ <field name="arch" type="xml">
+ <notebook>
+ <page string="Deliveries" position="inside">
+ <field colspan="4" mode="tree,from" name="delivery_line" nolabel="1" widget="one2many_list">
+ <form string="Delivery Lines">
+ <field name="product_id"
+ />
+ <field name="product_qty"
+ />
+ <field name="product_uom"
+ />
+ <field name="packaging_id"
+ />
+ <field name="date_planned"/>
+ <field name="priority"/>
+ <separator colspan="4" string="Notes"/>
+ <field name="note" colspan="4" nolabel="1"/>
+ </form>
+ <tree string="Delivery Lines">
+ <field name="product_id"/>
+ <field name="product_qty"/>
+ <field name="product_uom"/>
+ <field name="packaging_id"/>
+ <field name="date_planned"/>
+ <field name="priority"/>
+ <field name="note"/>
+ </tree>
+ </field>
+ </page>
+ </notebook>
+ </field>
+ </record>
+
+ <record model="ir.ui.view" id="view_order_line_tree_inherited">
+ <field name="name">sale.order.line.form1</field>
+ <field name="model">sale.order.line</field>
+ <field name="type">tree</field>
+ <field name="inherit_id" ref="sale.view_order_line_tree"/>
+ <field name="arch" type="xml">
+ <field name="price_subtotal" position="after">
+ <field name="deliveries"/>
+ </field>
+ </field>
+ </record>
+
+
+ <record id="view_sale_delivery_form1" model="ir.ui.view">
+ <field name="name">sale.delivery.form.inherit1</field>
+ <field name="model">sale.order</field>
+ <field name="type">form</field>
+ <field name="inherit_id" ref="sale.view_order_form"/>
+ <field name="arch" type="xml">
+ <field name="price_subtotal" position="after">
+ <field name="deliveries"/>
+ </field>
+ </field>
+ </record>
+
+ </data>
+</openerp>
_description = "Packing list"
def _set_maximum_date(self, cr, uid, ids, name, value, arg, context):
if not value: return False
+<<<<<<< TREE
+ for pick in self.browse(cr, uid, [ids], context):
+=======
if isinstance(ids, (int, long)):
ids=[ids]
for pick in self.browse(cr, uid, ids, context):
+>>>>>>> MERGE-SOURCE
cr.execute("""update stock_move set
date_planned=%s
where
picking_id=%d and
- (date_planned=%s or date_planned>%s)""", (value,pick.id,pick.max_date,value))
+ (date_planned>%s)""", (value,pick.id,value))
return True
def _set_minimum_date(self, cr, uid, ids, name, value, arg, context):
if not value: return False
+<<<<<<< TREE
+ for pick in self.browse(cr, uid,[ids], context):
+=======
if isinstance(ids, (int, long)):
ids=[ids]
for pick in self.browse(cr, uid, ids, context):
+>>>>>>> MERGE-SOURCE
cr.execute("""update stock_move set
date_planned=%s
where
picking_id=%d and
- (date_planned=%s or date_planned<%s)""", (value,pick.id,pick.min_date,value))
+ (date_planned<%s)""", (value,pick.id,value))
return True
def get_min_max_date(self, cr, uid, ids, field_name, arg, context={}):
#def copy(self, cr, uid, id, data=None, context={}):
# data = data or {}
# return super(stock_picking, self).copy(cr, uid, id, data, context)
-
+ def unlink(self, cr, uid, ids):
+ pickings = self.read(cr, uid, ids, ['state'])
+ unlink_ids = []
+ for s in pickings:
+ if s['state'] in ['draft','cancel']:
+ unlink_ids.append(s['id'])
+ else:
+ raise osv.except_osv(_('Invalid action !'), _('Cannot delete Picking(s) which are in %s State!' % s['state']))
+ osv.osv.unlink(self, cr, uid, unlink_ids)
+ return True
+
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 { }
'state': fields.selection([('draft','Draft'),('waiting','Waiting'),('confirmed','Confirmed'),('assigned','Assigned'),('done','Done'),('cancel','cancel')], 'Status', readonly=True, select=True),
'price_unit': fields.float('Unit Price',
digits=(16, int(config['price_accuracy']))),
+ 'partner_id':fields.related('picking_id','address_id',type='many2one',relation='res.partner.address', string='Partner'),
}
_constraints = [
'You try to assign a lot which is not from the same product',
['prodlot_id'])]
+# def unlink(self, cr, uid, ids):
+# moves = self.read(cr, uid, ids, ['state'])
+# unlink_ids = []
+# for s in moves:
+# if s['state'] in ['draft','cancel']:
+# unlink_ids.append(s['id'])
+# else:
+# raise osv.except_osv(_('Invalid action !'), _('Cannot delete Stock Move(s) which are in %s State!' % s['state']))
+# osv.osv.unlink(self, cr, uid, unlink_ids)
+# return True
+
def _default_location_destination(self, cr, uid, context={}):
if context.get('move_line', []):
return context['move_line'][0][2]['location_dest_id']
<form string="Move Lines">
<field name="address_id" invisible="True" context="{'context_display':'partner'}"/>
<field name="picking_id" invisible="True"/>
- <field domain="[('picking_id','<>',picking_id),('state','in',['confirmed','assigned']),('picking_id.address_id','=',address_id)]" name="move_ids" nolabel="1"/><newline/>
+ <field domain="[('picking_id','<>',picking_id),('state','in',['confirmed','assigned'])]" name="move_ids" select="1"/><newline/>
<group colspan="4">
<button special="cancel" string="Cancel"/>
<button name="action_move" string="Add" type="object"/>
<field eval="'ir.actions.wizard,%d'%ref('location_product')" name="value"/>
<field eval="True" name="object"/>
</record>
+
-
-
+ <record id="report_stock_latest_tree" model="ir.ui.view">
+ <field name="name">report.inventory.latest.view</field>
+ <field name="model">report.inventory.latest</field>
+ <field name="type">tree</field>
+ <field name="arch" type="xml">
+ <tree string="Latest inventories by product">
+ <field name="product" select="1"/>
+ <field name="qty" select="1"/>
+ <field name="uom"/>
+ <field name="inventory"/>
+ <field name="name"/>
+ </tree>
+ </field>
+ </record>
+
+ <record id="action_report_stock_latest_form" model="ir.actions.act_window">
+ <field name="name">Latest Inventories by product</field>
+ <field name="type">ir.actions.act_window</field>
+ <field name="res_model">report.inventory.latest</field>
+ <field name="view_type">form</field>
+ <field name="view_mode">tree</field>
+ <field name="domain">[('name','>=',time.strftime('%Y-%m-01'))]</field>
+ </record>
+ <menuitem
+ id="next_id_61"
+ name="Reporting"
+ parent="stock.menu_stock_root"/>
+ <menuitem action="action_report_stock_latest_form" id="menu_report_stock_latest" parent="next_id_61"/>
+
</data>
</openerp>