diff --git a/addons/mrp/mrp.py b/addons/mrp/mrp.py
index 351e5fc8d2d..80574cf4a4d 100644
--- a/addons/mrp/mrp.py
+++ b/addons/mrp/mrp.py
@@ -53,11 +53,23 @@ class mrp_workcenter(osv.osv):
'costs_journal_id': fields.many2one('account.analytic.journal', 'Analytic Journal'),
'costs_general_account_id': fields.many2one('account.account', 'General Account', domain=[('type','<>','view')]),
'resource_id': fields.many2one('resource.resource','Resource', ondelete='cascade', required=True),
+ 'product_id': fields.many2one('product.product','Workcenter Product', help="Fill this product to track easily your production costs in the analytic accounting."),
}
_defaults = {
'capacity_per_cycle': 1.0,
'resource_type': 'material',
}
+
+ def on_change_product_cost(self, cr, uid, ids, product_id, context=None):
+ if context is None:
+ context = {}
+ value = {}
+
+ if product_id:
+ cost = self.pool.get('product.product').browse(cr, uid, product_id, context=context)
+ value = {'costs_hour' :cost.standard_price}
+ return {'value': value}
+
mrp_workcenter()
@@ -99,8 +111,8 @@ class mrp_routing_workcenter(osv.osv):
'name': fields.char('Name', size=64, required=True),
'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of routing workcenters."),
'cycle_nbr': fields.float('Number of Cycles', required=True,
- help="Number of operations this workcenter can do."),
- 'hour_nbr': fields.float('Number of Hours', required=True, help="Time in hours for doing one cycle."),
+ help="Number of iterations this workcenter has to do in the specified operation of the routing."),
+ 'hour_nbr': fields.float('Number of Hours', required=True, help="Time in hours for this workcenter to achieve the operation of the specified routing."),
'routing_id': fields.many2one('mrp.routing', 'Parent Routing', select=True, ondelete='cascade',
help="Routing indicates all the workcenters used, for how long and/or cycles." \
"If Routing is indicated then,the third tab of a production order (workcenters) will be automatically pre-completed."),
@@ -746,7 +758,10 @@ class mrp_production(osv.osv):
'account_id': account,
'general_account_id': wc.costs_general_account_id.id,
'journal_id': wc.costs_journal_id.id,
- 'code': wc.code
+ 'ref': wc.code,
+ 'product_id': wc.product_id.id,
+ 'unit_amount': wc_line.hour,
+ 'product_uom_id': wc.product_id.uom_id.id
} )
if wc.costs_journal_id and wc.costs_general_account_id:
value = wc_line.cycle * wc.costs_cycle
@@ -759,7 +774,10 @@ class mrp_production(osv.osv):
'account_id': account,
'general_account_id': wc.costs_general_account_id.id,
'journal_id': wc.costs_journal_id.id,
- 'code': wc.code,
+ 'ref': wc.code,
+ 'product_id': wc.product_id.id,
+ 'unit_amount': wc_line.cycle,
+ 'product_uom_id': wc.product_id.uom_id.id
} )
return amount
diff --git a/addons/mrp/mrp_view.xml b/addons/mrp/mrp_view.xml
index 1eb8570ca29..33be81d0a10 100644
--- a/addons/mrp/mrp_view.xml
+++ b/addons/mrp/mrp_view.xml
@@ -162,12 +162,13 @@
+
-
-
+
+
@@ -650,9 +651,9 @@
-
-
-
+
-
+
diff --git a/addons/mrp/wizard/mrp_price.py b/addons/mrp/wizard/mrp_price.py
index b136326a2dc..c392ee22131 100644
--- a/addons/mrp/wizard/mrp_price.py
+++ b/addons/mrp/wizard/mrp_price.py
@@ -25,7 +25,7 @@ class mrp_price(osv.osv_memory):
_name = 'mrp.product_price'
_description = 'Product Price'
_columns = {
- 'number': fields.integer('Quantity', required=True, help="Specify quantity of products to produce or buy. Report of Cost structure will be displayed base on this qunatity."),
+ 'number': fields.integer('Quantity', required=True, help="Specify quantity of products to produce or buy. Report of Cost structure will be displayed base on this quantity."),
}
_defaults = {
'number': 1,
diff --git a/addons/procurement/procurement.py b/addons/procurement/procurement.py
index 23cf0a18ff5..ae7bf4183c6 100644
--- a/addons/procurement/procurement.py
+++ b/addons/procurement/procurement.py
@@ -119,11 +119,11 @@ class procurement_order(osv.osv):
'company_id': fields.many2one('res.company','Company',required=True),
}
_defaults = {
- 'state': lambda *a: 'draft',
- 'priority': lambda *a: '1',
+ 'state': 'draft',
+ 'priority': '1',
'date_planned': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
- 'close_move': lambda *a: 0,
- 'procure_method': lambda *a: 'make_to_order',
+ 'close_move': 0,
+ 'procure_method': 'make_to_order',
'company_id': lambda self, cr, uid, c: self.pool.get('res.company')._company_default_get(cr, uid, 'procurement.order', context=c)
}
@@ -490,6 +490,16 @@ class stock_warehouse_orderpoint(osv.osv):
_name = "stock.warehouse.orderpoint"
_description = "Minimum Inventory Rule"
+ def _get_draft_procurements(self, cr, uid, ids, field_name, arg, context=None):
+ if context is None:
+ context = {}
+ result = {}
+ procurement_obj = self.pool.get('procurement.order')
+ for orderpoint in self.browse(cr, uid, ids, context=context):
+ procurement_ids = procurement_obj.search(cr, uid , [('state', '=', 'draft'), ('product_id', '=', orderpoint.product_id.id), ('location_id', '=', orderpoint.location_id.id)])
+ result[orderpoint.id] = procurement_ids
+ return result
+
_columns = {
'name': fields.char('Name', size=32, required=True),
'active': fields.boolean('Active', help="If the active field is set to False, it will allow you to hide the orderpoint without removing it."),
@@ -508,6 +518,8 @@ class stock_warehouse_orderpoint(osv.osv):
help="The procurement quantity will by rounded up to this multiple."),
'procurement_id': fields.many2one('procurement.order', 'Latest procurement', ondelete="set null"),
'company_id': fields.many2one('res.company','Company',required=True),
+ 'procurement_draft_ids': fields.function(_get_draft_procurements, method=True, type='many2many', relation="procurement.order", \
+ string="Related Procurement Orders",help="Draft procurement of the product and location of that orderpoint"),
}
_defaults = {
'active': lambda *a: 1,
diff --git a/addons/procurement/procurement_view.xml b/addons/procurement/procurement_view.xml
index a2274d3326a..83376678fea 100644
--- a/addons/procurement/procurement_view.xml
+++ b/addons/procurement/procurement_view.xml
@@ -4,7 +4,7 @@
-
+
procurement.order.tree
@@ -145,7 +145,7 @@
Procurement Orders represent the need for a certain quantity of products, at a given time, in a given location. Sale Orders are one typical source of Procurement Orders (but these are distinct documents). Depending on the procurement parameters and the products configuration, the procurement engine will attempt to satisfy the need by reserving products from stock, or ordering products from a supplier, or passing a manufacturing order, etc. A Procurement Exception occurs when the system cannot find a way to fulfill a procurement. Some exceptions will resolve themselves automatically, but others require manual intervention (those are identified by a specific error message)
-
+
Procurement Exceptions
ir.actions.act_window
@@ -224,6 +224,10 @@
+
+
+
+
@@ -238,11 +242,11 @@
You can define your minimum stock rules, so that OpenERP will trigger automatically the propositions of manufacturing or purchase orders according to the stock level. Once the virtual stock of a product (=stock on hand minus all confirmed orders and reservations) is bellow the minimum quantity, OpenERP will generate a procurement request in order to fullfil the stock up to the maximum quantity.
-
diff --git a/addons/procurement/schedulers.py b/addons/procurement/schedulers.py
index b3d3c3bb98d..9b3704e8df7 100644
--- a/addons/procurement/schedulers.py
+++ b/addons/procurement/schedulers.py
@@ -34,14 +34,14 @@ class procurement_order(osv.osv):
def _procure_confirm(self, cr, uid, ids=None, use_new_cursor=False, context=None):
'''
Call the scheduler to check the procurement order
-
+
@param self: The object pointer
@param cr: The current row, from the database cursor,
@param uid: The current user ID for security checks
@param ids: List of selected IDs
@param use_new_cursor: False or the dbname
@param context: A standard dictionary for contextual values
- @return: Dictionary of values
+ @return: Dictionary of values
'''
if not context:
context = {}
@@ -50,7 +50,7 @@ class procurement_order(osv.osv):
if use_new_cursor:
cr = pooler.get_db(use_new_cursor).cursor()
wf_service = netsvc.LocalService("workflow")
-
+
procurement_obj = self.pool.get('procurement.order')
if not ids:
ids = procurement_obj.search(cr, uid, [], order="date_planned")
@@ -112,13 +112,13 @@ class procurement_order(osv.osv):
if uid:
request = self.pool.get('res.request')
summary = '''Here is the procurement scheduling report.
-
+
Start Time: %s
End Time: %s
Total Procurements processed: %d
Procurements with exceptions: %d
Skipped Procurements (scheduled date outside of scheduler range) %d
-
+
Exceptions:\n'''% (start_date, end_date, report_total, report_except, report_later)
summary += '\n'.join(report)
request.create(cr, uid,
@@ -141,7 +141,7 @@ class procurement_order(osv.osv):
def create_automatic_op(self, cr, uid, context=None):
"""
Create procurement of virtual stock < 0
-
+
@param self: The object pointer
@param cr: The current row, from the database cursor,
@param uid: The current user ID for security checks
@@ -157,7 +157,9 @@ class procurement_order(osv.osv):
warehouse_ids = warehouse_obj.search(cr, uid, [], context=context)
- cr.execute('select id from product_product')
+ cr.execute('select p.id from product_product p \
+ join product_template t on (p.product_tmpl_id=t.id) \
+ where p.active=True and t.purchase_ok=True')
products_id = [x for x, in cr.fetchall()]
for warehouse in warehouse_obj.browse(cr, uid, warehouse_ids, context=context):
@@ -191,14 +193,14 @@ class procurement_order(osv.osv):
'''
Create procurement based on Orderpoint
use_new_cursor: False or the dbname
-
+
@param self: The object pointer
@param cr: The current row, from the database cursor,
@param user_id: The current user ID for security checks
@param context: A standard dictionary for contextual values
@param param: False or the dbname
@return: Dictionary of values
- """
+ """
'''
if not context:
context = {}
@@ -212,27 +214,52 @@ class procurement_order(osv.osv):
report = []
offset = 0
ids = [1]
+ newdate = datetime.today()
if automatic:
self.create_automatic_op(cr, uid, context=context)
while ids:
ids = orderpoint_obj.search(cr, uid, [], offset=offset, limit=100)
- for op in orderpoint_obj.browse(cr, uid, ids):
+ for op in orderpoint_obj.browse(cr, uid, ids, context=context):
if op.procurement_id.state != 'exception':
if op.procurement_id and op.procurement_id.purchase_id and op.procurement_id.purchase_id.state in ('draft', 'confirmed'):
continue
prods = location_obj._product_virtual_get(cr, uid,
op.location_id.id, [op.product_id.id],
{'uom': op.product_uom.id})[op.product_id.id]
+
if prods < op.product_min_qty:
qty = max(op.product_min_qty, op.product_max_qty)-prods
+
reste = qty % op.qty_multiple
if reste > 0:
qty += op.qty_multiple - reste
+
newdate = datetime.today() + relativedelta(
days = int(op.product_id.seller_delay))
if qty <= 0:
continue
if op.product_id.type not in ('consu'):
+ # Check draft procurement related to this order point
+ if op.procurement_draft_ids:
+ procure_qty = {}
+ #Compute list of draft procurement attached to Orderpoint
+ for pro_data in op.procurement_draft_ids:
+ procure_qty.update({pro_data.product_qty: pro_data.id })
+ procure_list = procure_qty.keys()
+ procure_list.sort()
+ procure_list.reverse()
+
+ to_generate = qty
+ for proc in procure_list:
+ if to_generate >= proc:
+ wf_service.trg_validate(uid, 'procurement.order', procure_qty[proc], 'button_confirm', cr)
+ procurement_obj.write(cr, uid, [procure_qty[proc]], {'origin': op.name}, context=context)
+ to_generate -= proc
+ if not to_generate:
+ break
+ qty = to_generate
+
+ if qty:
proc_id = procurement_obj.create(cr, uid, {
'name': op.name,
'date_planned': newdate.strftime('%Y-%m-%d'),
@@ -248,7 +275,7 @@ class procurement_order(osv.osv):
wf_service.trg_validate(uid, 'procurement.order', proc_id,
'button_check', cr)
orderpoint_obj.write(cr, uid, [op.id],
- {'procurement_id': proc_id})
+ {'procurement_id': proc_id}, context=context)
offset += len(ids)
if use_new_cursor:
cr.commit()
diff --git a/addons/product/product.py b/addons/product/product.py
index e28fd175dc7..84b90a53b62 100644
--- a/addons/product/product.py
+++ b/addons/product/product.py
@@ -346,16 +346,7 @@ class product_template(osv.osv):
return False
return True
- def _check_uos(self, cursor, user, ids):
- for product in self.browse(cursor, user, ids):
- if product.uos_id \
- and product.uos_id.category_id.id \
- == product.uom_id.category_id.id:
- return False
- return True
-
_constraints = [
- (_check_uos, 'Error: UOS must be in a different category than the UOM', ['uos_id']),
(_check_uom, 'Error: The default UOM and the purchase UOM must be in the same category.', ['uom_id']),
]
diff --git a/addons/purchase/purchase_view.xml b/addons/purchase/purchase_view.xml
index 290ad0d4587..b0463957e9a 100644
--- a/addons/purchase/purchase_view.xml
+++ b/addons/purchase/purchase_view.xml
@@ -8,6 +8,18 @@
+
+
+
+
+
+
+
Addresses
@@ -162,7 +174,7 @@
-
+
@@ -312,12 +324,12 @@
-
+
-
-
-
+
+
+
@@ -375,7 +387,7 @@
-
+
diff --git a/addons/purchase/wizard/purchase_line_invoice.py b/addons/purchase/wizard/purchase_line_invoice.py
index f57ae82e51f..26dedb158a7 100644
--- a/addons/purchase/wizard/purchase_line_invoice.py
+++ b/addons/purchase/wizard/purchase_line_invoice.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
##############################################################################
-#
+#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL ().
#
@@ -15,7 +15,7 @@
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see .
+# along with this program. If not, see .
#
##############################################################################
@@ -26,26 +26,26 @@ from tools.translate import _
class purchase_line_invoice(osv.osv_memory):
-
+
""" To create invoice for purchase order line"""
-
+
_name = 'purchase.order.line_invoice'
_description = 'Purchase Order Line Make Invoice'
-
+
def makeInvoices(self, cr, uid, ids, context=None):
-
- """
+
+ """
To get Purchase Order line and create Invoice
@param self: The object pointer.
@param cr: A database cursor
@param uid: ID of the user currently logged in
- @param context: A standard dictionary
+ @param context: A standard dictionary
@return : retrun view of Invoice
"""
-
+
if context is None:
context={}
-
+
record_ids = context.get('active_ids',[])
if record_ids:
res = False
@@ -55,28 +55,28 @@ class purchase_line_invoice(osv.osv_memory):
property_obj=self.pool.get('ir.property')
account_fiscal_obj=self.pool.get('account.fiscal.position')
invoice_line_obj=self.pool.get('account.invoice.line')
-
-
+ account_jrnl_obj=self.pool.get('account.journal')
+
def multiple_order_invoice_name(orders):
name = "PO";
for order in orders:
name += "-%d" % order.id
return name
-
+
def multiple_order_invoice_reference(partner, orders):
reference = "P%dPO" % partner.id
- for order in orders:
+ for order in orders:
reference += "-%d" % order.id
return reference
-
+
def multiple_order_invoice_notes(orders):
notes = ""
for order in orders:
notes += "%s \n" % order.notes
return notes
-
-
-
+
+
+
def make_invoice_by_partner(partner, orders, lines_ids):
"""
create a new invoice for one supplier
@@ -84,23 +84,25 @@ class purchase_line_invoice(osv.osv_memory):
@param orders : The set of orders to add in the invoice
@param lines : The list of line's id
"""
-
+ journal_id = account_jrnl_obj.search(cr, uid, [('type', '=', 'purchase')], context=None)
+ journal_id = journal_id and journal_id[0] or False
a = partner.property_account_payable.id
if partner and partner.property_payment_term.id:
pay_term = partner.property_payment_term.id
else:
pay_term = False
inv = {
- 'name': multiple_order_invoice_name(orders),
- 'origin': multiple_order_invoice_name(orders),
+ 'name': multiple_order_invoice_name(orders),
+ 'origin': multiple_order_invoice_name(orders),
'type': 'in_invoice',
+ 'journal_id':journal_id,
'reference': multiple_order_invoice_reference(partner, orders),
'account_id': a,
'partner_id': partner.id,
'address_invoice_id': orders[0].partner_address_id.id,
'address_contact_id': orders[0].partner_address_id.id,
'invoice_line': [(6,0,lines_ids)],
- 'currency_id' : orders[0].pricelist_id.currency_id.id,
+ 'currency_id' : orders[0].pricelist_id.currency_id.id,
'comment': multiple_order_invoice_notes(orders),
'payment_term': pay_term,
'fiscal_position': partner.property_account_position.id
@@ -136,7 +138,7 @@ class purchase_line_invoice(osv.osv_memory):
'uos_id': line.product_uom.id,
'product_id': line.product_id.id or False,
'invoice_line_tax_id': [(6, 0, [x.id for x in line.taxes_id])],
- 'note': line.notes,
+ 'note': line.notes,
'account_analytic_id': line.account_analytic_id and line.account_analytic_id.id or False,
})
cr.execute('insert into purchase_order_line_invoice_rel (order_line_id,invoice_id) values (%s,%s)', (line.id, inv_id))
@@ -147,7 +149,7 @@ class purchase_line_invoice(osv.osv_memory):
for result in invoices.values():
il = map(lambda x: x[1], result)
orders = list(set(map(lambda x : x[0].order_id, result)))
-
+
res.append(make_invoice_by_partner(orders[0].partner_id, orders, il))
return {
diff --git a/addons/report_webkit/ir_report.py b/addons/report_webkit/ir_report.py
index b3155b25430..8f89fb51698 100644
--- a/addons/report_webkit/ir_report.py
+++ b/addons/report_webkit/ir_report.py
@@ -38,8 +38,12 @@ def register_report(name, model, tmpl_path, parser=rml_parse):
"Register the report into the services"
name = 'report.%s' % name
if netsvc.Service._services.get(name, False):
- if hasattr(netsvc.Service._services[name], 'parser'):
- parser = netsvc.Service._services[name].parser
+ service = netsvc.Service._services[name]
+ if isinstance(service, WebKitParser):
+ #already instantiated properly, skip it
+ return
+ if hasattr(service, 'parser'):
+ parser = service.parser
del netsvc.Service._services[name]
WebKitParser(name, model, tmpl_path, parser=parser)
diff --git a/addons/report_webkit/report_helper.py b/addons/report_webkit/report_helper.py
index 80896fa4ba2..db0c1c372fc 100644
--- a/addons/report_webkit/report_helper.py
+++ b/addons/report_webkit/report_helper.py
@@ -51,7 +51,7 @@ class WebKitHelper(object):
height = 'width="%spx"'%(height)
else :
height = ' '
- toreturn = ' '%(
+ toreturn = ' '%(
width,
height,
type,
diff --git a/addons/report_webkit/webkit_report.py b/addons/report_webkit/webkit_report.py
index c95a9ce5e2e..2944b768643 100644
--- a/addons/report_webkit/webkit_report.py
+++ b/addons/report_webkit/webkit_report.py
@@ -35,7 +35,7 @@ import report
import tempfile
import time
from mako.template import Template
-
+from mako import exceptions
import netsvc
import pooler
from report_helper import WebKitHelper
@@ -84,8 +84,10 @@ class WebKitParser(report_sxw):
_('path to Wkhtmltopdf is not absolute'),
'for path %s'%(path)
)
- def generate_pdf(self, comm_path, report_xml, header, footer, html_list):
+ def generate_pdf(self, comm_path, report_xml, header, footer, html_list, webkit_header=False):
"""Call webkit in order to generate pdf"""
+ if not webkit_header:
+ webkit_header = report_xml.webkit_header
tmp_dir = tempfile.gettempdir()
out = report_xml.name+str(time.time())+'.pdf'
out = os.path.join(tmp_dir, out.replace(' ',''))
@@ -95,7 +97,7 @@ class WebKitParser(report_sxw):
command = [comm_path]
else:
command = ['wkhtmltopdf']
-
+
command.append('-q')
if header :
head_file = file( os.path.join(
@@ -120,20 +122,22 @@ class WebKitParser(report_sxw):
file_to_del.append(foot_file.name)
command.append("--footer-html '%s'"%(foot_file.name))
- if report_xml.webkit_header.margin_top :
- command.append('--margin-top %s'%(report_xml.webkit_header.margin_top))
- if report_xml.webkit_header.margin_bottom :
- command.append('--margin-bottom %s'%(report_xml.webkit_header.margin_bottom))
- if report_xml.webkit_header.margin_left :
- command.append('--margin-left %s'%(report_xml.webkit_header.margin_left))
- if report_xml.webkit_header.margin_right :
- command.append('--margin-right %s'%(report_xml.webkit_header.margin_right))
- if report_xml.webkit_header.orientation :
- command.append("--orientation '%s'"%(report_xml.webkit_header.orientation))
- if report_xml.webkit_header.format :
- command.append(" --page-size '%s'"%(report_xml.webkit_header.format))
+ if webkit_header.margin_top :
+ command.append('--margin-top %s'%(webkit_header.margin_top))
+ if webkit_header.margin_bottom :
+ command.append('--margin-bottom %s'%(webkit_header.margin_bottom))
+ if webkit_header.margin_left :
+ command.append('--margin-left %s'%(webkit_header.margin_left))
+ if webkit_header.margin_right :
+ command.append('--margin-right %s'%(webkit_header.margin_right))
+ if webkit_header.orientation :
+ command.append("--orientation '%s'"%(webkit_header.orientation))
+ if webkit_header.format :
+ command.append(" --page-size '%s'"%(webkit_header.format))
+ count = 0
for html in html_list :
- html_file = file(os.path.join(tmp_dir, str(time.time()) + '.body.html'), 'w')
+ html_file = file(os.path.join(tmp_dir, str(time.time()) + str(count) +'.body.html'), 'w')
+ count += 1
html_file.write(html)
html_file.close()
file_to_del.append(html_file.name)
@@ -168,6 +172,8 @@ class WebKitParser(report_sxw):
"""Translate String."""
ir_translation = self.pool.get('ir.translation')
res = ir_translation._get_source(self.parser_instance.cr, self.parser_instance.uid, self.name, 'report', self.localcontext.get('lang', 'en_US'), src)
+ if not res :
+ return src
return res
def formatLang(self, value, digits=None, date=False, date_time=False, grouping=True, monetary=False):
@@ -207,14 +213,14 @@ class WebKitParser(report_sxw):
if context is None:
context={}
-
+
if report_xml.report_type != 'webkit':
return super(WebKitParser,self).create_single_pdf(cursor, uid, ids, data, report_xml, context=context)
self.parser_instance = self.parser(
- cursor,
- uid,
- self.name2,
+ cursor,
+ uid,
+ self.name2,
context=context
)
@@ -272,45 +278,64 @@ class WebKitParser(report_sxw):
#default_filters=['unicode', 'entity'] can be used to set global filter
body_mako_tpl = Template(template ,input_encoding='utf-8')
helper = WebKitHelper(cursor, uid, report_xml.id, context)
- html = body_mako_tpl.render( helper=helper,
- css=css,
- _=self.translate_call,
- **self.parser_instance.localcontext
- )
+ try :
+ html = body_mako_tpl.render( helper=helper,
+ css=css,
+ _=self.translate_call,
+ **self.parser_instance.localcontext
+ )
+ except Exception, e:
+ msg = exceptions.text_error_template().render()
+ netsvc.Logger().notifyChannel('Webkit render', netsvc.LOG_ERROR, msg)
+ raise except_osv(_('Webkit render'), msg)
head_mako_tpl = Template(header, input_encoding='utf-8')
- head = head_mako_tpl.render(
- company=company,
- time=time,
- helper=helper,
- css=css,
- formatLang=self.formatLang,
- setLang=self.setLang,
- _=self.translate_call,
- _debug=False
- )
+ try :
+ head = head_mako_tpl.render(
+ company=company,
+ time=time,
+ helper=helper,
+ css=css,
+ formatLang=self.formatLang,
+ setLang=self.setLang,
+ _=self.translate_call,
+ _debug=False
+ )
+ except Exception, e:
+ raise except_osv(_('Webkit render'),
+ exceptions.text_error_template().render())
foot = False
if footer :
foot_mako_tpl = Template(footer ,input_encoding='utf-8')
- foot = foot_mako_tpl.render(
- company=company,
- time=time,
- helper=helper,
- css=css,
- formatLang=self.formatLang,
- setLang=self.setLang,
- _=self.translate_call,
- )
+ try :
+ foot = foot_mako_tpl.render(
+ company=company,
+ time=time,
+ helper=helper,
+ css=css,
+ formatLang=self.formatLang,
+ setLang=self.setLang,
+ _=self.translate_call,
+ )
+ except:
+ msg = exceptions.text_error_template().render()
+ netsvc.Logger().notifyChannel('Webkit render', netsvc.LOG_ERROR, msg)
+ raise except_osv(_('Webkit render'), msg)
if report_xml.webkit_debug :
- deb = head_mako_tpl.render(
- company=company,
- time=time,
- helper=helper,
- css=css,
- _debug=html,
- formatLang=self.formatLang,
- setLang=self.setLang,
- _=self.translate_call,
- )
+ try :
+ deb = head_mako_tpl.render(
+ company=company,
+ time=time,
+ helper=helper,
+ css=css,
+ _debug=html,
+ formatLang=self.formatLang,
+ setLang=self.setLang,
+ _=self.translate_call,
+ )
+ except Exception, e:
+ msg = exceptions.text_error_template().render()
+ netsvc.Logger().notifyChannel('Webkit render', netsvc.LOG_ERROR, msg)
+ raise except_osv(_('Webkit render'), msg)
return (deb, 'html')
bin = self.get_lib(cursor, uid, company.id)
pdf = self.generate_pdf(bin, report_xml, head, foot, [html])
diff --git a/addons/stock/__openerp__.py b/addons/stock/__openerp__.py
index 0b85511050d..987a0e12493 100644
--- a/addons/stock/__openerp__.py
+++ b/addons/stock/__openerp__.py
@@ -45,10 +45,11 @@ Thanks to the double entry management, the inventory controlling is powerful and
"init_xml" : [],
"demo_xml" : ["stock_demo.xml"],
"update_xml" : [
- "security/stock_security.xml",
- "security/ir.model.access.csv",
+ "security/stock_security.xml",
+ "security/ir.model.access.csv",
"stock_data.xml",
"wizard/stock_move_view.xml",
+ "wizard/stock_change_product_qty_view.xml",
"wizard/stock_partial_picking_view.xml",
"wizard/stock_partial_move_view.xml",
"wizard/stock_fill_inventory_view.xml",
diff --git a/addons/stock/product_view.xml b/addons/stock/product_view.xml
index 35715a9e1fb..05b2f83c15b 100644
--- a/addons/stock/product_view.xml
+++ b/addons/stock/product_view.xml
@@ -102,9 +102,12 @@
-
+
+
+
diff --git a/addons/stock/stock.py b/addons/stock/stock.py
index fd0c8597a6e..6723c1b142b 100644
--- a/addons/stock/stock.py
+++ b/addons/stock/stock.py
@@ -110,6 +110,7 @@ class stock_location(osv.osv):
@param field_names: Name of field
@return: Dictionary of values
"""
+ prod_id = context and context.get('product_id', False)
product_product_obj = self.pool.get('product.product')
@@ -122,9 +123,10 @@ class stock_location(osv.osv):
currency_id = self.pool.get('res.users').browse(cr, uid, uid).company_id.currency_id.id
currency_obj = self.pool.get('res.currency')
- currency = self.pool.get('res.currency').browse(cr, uid, currency_id)
- currency_obj.round(cr, uid, currency, 300)
+ currency = currency_obj.browse(cr, uid, currency_id, context=context)
for loc_id, product_ids in products_by_location.items():
+ if prod_id:
+ product_ids = [prod_id]
c = (context or {}).copy()
c['location'] = loc_id
for prod in product_product_obj.browse(cr, uid, product_ids, context=c):
@@ -1494,8 +1496,8 @@ class stock_move(osv.osv):
'location_dest_id': fields.many2one('stock.location', 'Destination Location', required=True,states={'done': [('readonly', True)]}, select=True, help="Location where the system will stock the finished products."),
'address_id': fields.many2one('res.partner.address', 'Destination Address', help="Optional address where goods are to be delivered, specifically used for allotment"),
- 'prodlot_id': fields.many2one('stock.production.lot', 'Production Lot', help="Production lot is used to put a serial number on the production", select=True),
- 'tracking_id': fields.many2one('stock.tracking', 'Pack', select=True, help="Logistical shipping unit: pallet, box, pack ..."),
+ 'prodlot_id': fields.many2one('stock.production.lot', 'Production Lot', states={'done': [('readonly', True)]}, help="Production lot is used to put a serial number on the production", select=True),
+ 'tracking_id': fields.many2one('stock.tracking', 'Pack', select=True, states={'done': [('readonly', True)]}, help="Logistical shipping unit: pallet, box, pack ..."),
'auto_validate': fields.boolean('Auto Validate'),
@@ -1930,6 +1932,12 @@ class stock_move(osv.osv):
acc_variation = accounts.get('property_stock_variation', False)
journal_id = accounts['stock_journal']
+ if acc_dest == acc_variation:
+ raise osv.except_osv(_('Error!'), _('Can not create Journal Entry, Output Account defined on this product and Variant account on category of this product is same.'))
+
+ if acc_src == acc_variation:
+ raise osv.except_osv(_('Error!'), _('Can not create Journal Entry, Input Account defined on this product and Variant account on category of this product is same.'))
+
if not acc_src:
raise osv.except_osv(_('Error!'), _('There is no stock input account defined for this product or its category: "%s" (id: %d)') % \
(move.product_id.name, move.product_id.id,))
@@ -2442,6 +2450,12 @@ class stock_inventory(osv.osv):
return self.pool.get('stock.move').create(cr, uid, move_vals)
def action_done(self, cr, uid, ids, context=None):
+ """ Finished the inventory
+ @return: True
+ """
+
+ if context is None:
+ context = {}
move_obj = self.pool.get('stock.move')
for inv in self.browse(cr, uid, ids, context=context):
move_obj.action_done(cr, uid, [x.id for x in inv.move_ids], context=context)
@@ -2449,18 +2463,17 @@ class stock_inventory(osv.osv):
return True
def action_confirm(self, cr, uid, ids, context=None):
- """ Finishes the inventory and writes its finished date
+ """ Confirm the inventory and writes its finished date
@return: True
"""
if context is None:
context = {}
-
# to perform the correct inventory corrections we need analyze stock location by
# location, never recursively, so we use a special context
product_context = dict(context, compute_child=False)
location_obj = self.pool.get('stock.location')
- for inv in self.browse(cr, uid, ids):
+ for inv in self.browse(cr, uid, ids, context=context):
move_ids = []
for line in inv.inventory_line_id:
pid = line.product_id.id
diff --git a/addons/stock/stock_view.xml b/addons/stock/stock_view.xml
index b649e7feec8..feb7cc178fe 100644
--- a/addons/stock/stock_view.xml
+++ b/addons/stock/stock_view.xml
@@ -670,15 +670,15 @@
attrs="{'invisible': [('tracking_id','<>',False)]}"
icon="terp-stock_effects-object-colorize"
groups="base.group_extended"
- states="draft,assigned,confirmed,done"/>
+ states="draft,assigned,confirmed"/>
+ states="draft,assigned,confirmed"/>
+ states="draft,assigned,confirmed"/>
@@ -719,14 +719,14 @@
+ states="draft,assigned,confirmed" colspan="1"/>
@@ -859,18 +859,18 @@
+ states="draft,assigned,confirmed"/>
+ states="draft,assigned,confirmed"/>
@@ -912,14 +912,14 @@
+ states="draft,assigned,confirmed" colspan="1"/>
@@ -1089,7 +1089,7 @@
+ states="draft,assigned,confirmed"/>
+ states="draft,assigned,confirmed" colspan="1"/>
@@ -1399,14 +1399,14 @@
+ states="draft,assigned,confirmed" colspan="1"/>
@@ -1758,7 +1758,7 @@
src_model="stock.location"/>
).
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+#
+##############################################################################
+
+from osv import fields, osv
+from tools.translate import _
+import time
+
+class stock_change_product_qty(osv.osv_memory):
+ _name = "stock.change.product.qty"
+ _description = "Change Product Quantity"
+ _columns = {
+ 'new_quantity': fields.float('Quantity', required=True),
+ 'warehouse_id': fields.many2one('stock.warehouse', 'Warehouse', required=True, ondelete="cascade"),
+ 'location_id': fields.many2one('stock.location', 'Location', required=True, ondelete="cascade", domain="[('usage', '=', 'internal')]"),
+ }
+
+ def onchange_warehouse_id(self, cr, uid, ids, warehouse_id, context={}):
+ """ Finds location id for changed warehouse.
+ @param warehouse_id: Changed id of warehouse.
+ @return: Dictionary of values.
+ """
+ if warehouse_id:
+ warehouse = self.pool.get('stock.warehouse').browse(cr, uid, warehouse_id, context)
+ val = {'location_id': warehouse.lot_stock_id.id}
+ return {'value': val}
+ return {}
+
+ def default_get(self, cr, uid, fields, context):
+ """ To get default values for the object.
+ @param self: The object pointer.
+ @param cr: A database cursor
+ @param uid: ID of the user currently logged in
+ @param fields: List of fields for which we want default values
+ @param context: A standard dictionary
+ @return: A dictionary which of fields with values.
+ """
+ res = super(stock_change_product_qty, self).default_get(cr, uid, fields, context=context)
+
+ wids = self.pool.get('stock.warehouse').search(cr, uid, [], context=context)
+ if 'warehouse_id' in fields:
+ res.update({'warehouse_id': wids and wids[0] or False})
+
+ if 'new_quantity' in fields:
+ res.update({'new_quantity': 1})
+ return res
+
+ def change_product_qty(self, cr, uid, ids, context=None):
+ """ Changes the Product Quantity by making a Physical Inventory.
+ @param self: The object pointer.
+ @param cr: A database cursor
+ @param uid: ID of the user currently logged in
+ @param ids: List of IDs selected
+ @param context: A standard dictionary
+ @return:
+ """
+ if context is None:
+ context = {}
+
+ move_ids = []
+ rec_id = context and context.get('active_id', False)
+ assert rec_id, _('Active ID is not set in Context')
+
+ inventry_obj = self.pool.get('stock.inventory')
+ inventry_line_obj = self.pool.get('stock.inventory.line')
+ prod_obj_pool = self.pool.get('product.product')
+
+ res_original = prod_obj_pool.browse(cr, uid, rec_id, context=context)
+ for data in self.browse(cr, uid, ids, context=context):
+ invntry_id = inventry_obj.create(cr , uid, {'name': 'INV:' + str(res_original.name)}, context=context)
+ line_data ={
+ 'inventory_id' : invntry_id,
+ 'product_qty' : data.new_quantity,
+ 'location_id' : data.location_id.id,
+ 'product_id' : rec_id,
+ 'product_uom' : res_original.uom_id.id,
+ 'company_id' : self.pool.get('res.users').browse(cr, uid, uid).company_id.id or False ,
+ }
+ line_id = inventry_line_obj.create(cr , uid, line_data, context=context)
+
+ inventry_obj.action_confirm(cr, uid, [invntry_id], context=context)
+ inventry_obj.action_done(cr, uid, [invntry_id], context=context)
+
+ return {}
+
+stock_change_product_qty()
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/stock/wizard/stock_change_product_qty_view.xml b/addons/stock/wizard/stock_change_product_qty_view.xml
new file mode 100644
index 00000000000..ee7b1b66b1d
--- /dev/null
+++ b/addons/stock/wizard/stock_change_product_qty_view.xml
@@ -0,0 +1,38 @@
+
+
+
+
+ Change Product Quantity
+ stock.change.product.qty
+ form
+
+
+
+
+
+
+ Change Product Quantity
+ ir.actions.act_window
+ stock.change.product.qty
+ form
+ form
+
+ new
+
+
+
+
+
diff --git a/addons/stock/wizard/stock_return_picking.py b/addons/stock/wizard/stock_return_picking.py
index cf6f7c4b1bd..a2d70b91a4b 100644
--- a/addons/stock/wizard/stock_return_picking.py
+++ b/addons/stock/wizard/stock_return_picking.py
@@ -56,16 +56,16 @@ class stock_return_picking(osv.osv_memory):
return res
def view_init(self, cr, uid, fields_list, context=None):
- """
+ """
Creates view dynamically and adding fields at runtime.
@param self: The object pointer.
@param cr: A database cursor
@param uid: ID of the user currently logged in
- @param context: A standard dictionary
+ @param context: A standard dictionary
@return: New arch of view with new columns.
"""
res = super(stock_return_picking, self).view_init(cr, uid, fields_list, context=context)
- record_id = context and context.get('active_id', False)
+ record_id = context and context.get('active_id', False)
if record_id:
pick_obj = self.pool.get('stock.picking')
pick = pick_obj.browse(cr, uid, record_id)
@@ -87,15 +87,15 @@ class stock_return_picking(osv.osv_memory):
if not valid_lines:
raise osv.except_osv(_('Warning !'), _("There are no products to return (only lines in Done state and not fully returned yet can be returned)!"))
return res
-
- def fields_view_get(self, cr, uid, view_id=None, view_type='form',
+
+ def fields_view_get(self, cr, uid, view_id=None, view_type='form',
context=None, toolbar=False, submenu=False):
- """
+ """
Changes the view dynamically
@param self: The object pointer.
@param cr: A database cursor
@param uid: ID of the user currently logged in
- @param context: A standard dictionary
+ @param context: A standard dictionary
@return: New arch of view.
"""
res = super(stock_return_picking, self).fields_view_get(cr, uid, view_id=view_id, view_type=view_type, context=context, toolbar=toolbar,submenu=False)
@@ -116,7 +116,7 @@ class stock_return_picking(osv.osv_memory):
quantity = m.product_qty
if m.state=='done' and quantity > return_history[m.id]:
arch_lst.append(' \n ' % (m.id,))
- res['fields']['return%s' % m.id]={'string':m.name, 'type':'float', 'required':True}
+ res['fields']['return%s' % m.id]={'string':m.name, 'type':'float', 'required':True}
res.setdefault('returns', []).append(m.id)
arch_lst.append(' \n ')
res['fields']['invoice_state']={'string':_('Invoicing'), 'type':'selection','required':True, 'selection':[('2binvoiced', _('To be refunded/invoiced')), ('none', _('No invoicing'))]}
@@ -129,26 +129,26 @@ class stock_return_picking(osv.osv_memory):
return res
def create_returns(self, cr, uid, ids, context):
- """
+ """
Creates return picking.
@param self: The object pointer.
@param cr: A database cursor
@param uid: ID of the user currently logged in
- @param ids: List of ids selected
- @param context: A standard dictionary
- @return: A dictionary which of fields with values.
- """
+ @param ids: List of ids selected
+ @param context: A standard dictionary
+ @return: A dictionary which of fields with values.
+ """
record_id = context and context.get('active_id', False) or False
move_obj = self.pool.get('stock.move')
pick_obj = self.pool.get('stock.picking')
uom_obj = self.pool.get('product.uom')
wf_service = netsvc.LocalService("workflow")
-
+
pick = pick_obj.browse(cr, uid, record_id)
data = self.read(cr, uid, ids[0])
new_picking = None
date_cur = time.strftime('%Y-%m-%d %H:%M:%S')
-
+
set_invoice_state_to_none = True
returned_lines = 0
for move in pick.move_lines:
@@ -159,22 +159,22 @@ class stock_return_picking(osv.osv_memory):
new_type = 'out'
else:
new_type = 'internal'
- new_picking = pick_obj.copy(cr, uid, pick.id, {'name':'%s (return)' % pick.name,
+ new_picking = pick_obj.copy(cr, uid, pick.id, {'name':'%s-return' % pick.name,
'move_lines':[], 'state':'draft', 'type':new_type,
'date':date_cur, 'invoice_state':data['invoice_state'],})
new_location=move.location_dest_id.id
if move.state=='done':
new_qty = data['return%s' % move.id]
returned_qty = move.product_qty
-
+
for rec in move.move_history_ids2:
returned_qty -= rec.product_qty
-
+
if returned_qty != new_qty:
set_invoice_state_to_none = False
if new_qty:
- returned_lines += 1
+ returned_lines += 1
new_move=move_obj.copy(cr, uid, move.id, {
'product_qty': new_qty,
'product_uos_qty': uom_obj._compute_qty(cr, uid, move.product_uom.id,
diff --git a/addons/stock/wizard/stock_splitinto.py b/addons/stock/wizard/stock_splitinto.py
index 171d7dd1e14..a3bf4acc8af 100644
--- a/addons/stock/wizard/stock_splitinto.py
+++ b/addons/stock/wizard/stock_splitinto.py
@@ -64,7 +64,7 @@ class stock_split_into(osv.osv_memory):
if quantity_rest>0:
quantity_rest = move.product_qty - quantity
tracking_id = track_obj.create(cr, uid, {}, context=context)
- if quantity==0.0:
+ if quantity == 0.0:
move_obj.write(cr, uid, [move.id], {'tracking_id': tracking_id}, context=context)
else:
default_val = {
diff --git a/addons/stock/wizard/stock_traceability.py b/addons/stock/wizard/stock_traceability.py
index 815f90810ed..3906fa4fabf 100644
--- a/addons/stock/wizard/stock_traceability.py
+++ b/addons/stock/wizard/stock_traceability.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
##############################################################################
-#
+#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL ().
#
@@ -15,34 +15,34 @@
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see .
+# along with this program. If not, see .
#
##############################################################################
from osv import fields, osv
class action_traceability(osv.osv_memory):
- """
+ """
This class defines a function action_traceability for wizard
"""
_name = "action.traceability"
_description = "Action traceability "
-
+
def action_traceability(self, cr, uid, ids, context={}):
""" It traces the information of a product
@param self: The object pointer.
@param cr: A database cursor
@param uid: ID of the user currently logged in
- @param ids: List of IDs selected
- @param context: A standard dictionary
+ @param ids: List of IDs selected
+ @param context: A standard dictionary
@return: A dictionary of values
"""
-
+ lot_id = ids
type1 = context['type'] or 'move_history_ids2'
field = context['field'] or 'tracking_id'
obj = self.pool.get('stock.move')
- ids = obj.search(cr, uid, [(field, 'in',context['active_ids'])])
+ ids = obj.search(cr, uid, [(field, 'in',lot_id)])
cr.execute('select id from ir_ui_view where model=%s and field_parent=%s and type=%s', ('stock.move', type1, 'tree'))
view_id = cr.fetchone()[0]
value = {
@@ -53,7 +53,7 @@ class action_traceability(osv.osv_memory):
'field_parent': type1,
'view_id': (view_id,'View'),
'type': 'ir.actions.act_window',
- 'nodestroy':True,
+ 'nodestroy':True,
}
return value