[MERGE]: Merge with lp:~openerp-dev/openobject-addons/trunk-dev-addons2
bzr revid: rpa@tinyerp.com-20101208092808-k4rxpuskhpcpohq9
This commit is contained in:
+22
-4
@@ -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
|
||||
|
||||
|
||||
+10
-9
@@ -162,12 +162,13 @@
|
||||
</group>
|
||||
<group col="2" colspan="2">
|
||||
<separator colspan="2" string="Costing Information"/>
|
||||
<field name="product_id" on_change="on_change_product_cost(product_id)"/>
|
||||
<field name="costs_hour"/>
|
||||
<field name="costs_hour_account_id" groups="analytic.group_analytic_accounting"/>
|
||||
<field name="costs_cycle"/>
|
||||
<field name="costs_cycle_account_id" groups="analytic.group_analytic_accounting"/>
|
||||
<field name="costs_journal_id" groups="analytic.group_analytic_accounting"/>
|
||||
<field name="costs_general_account_id" groups="analytic.group_analytic_accounting"/>
|
||||
<field name="costs_journal_id" attrs="{'required':['|',('costs_hour_account_id', '=', True),('costs_cycle_account_id', '=', True)]}" groups="analytic.group_analytic_accounting"/>
|
||||
<field name="costs_general_account_id" attrs="{ 'required':['|',('costs_cycle_account_id', '=', True),('costs_hour_account_id', '=', True)]}" groups="analytic.group_analytic_accounting"/>
|
||||
</group>
|
||||
<separator colspan="4" string="Description"/>
|
||||
<field colspan="4" name="note" nolabel="1"/>
|
||||
@@ -650,9 +651,9 @@
|
||||
<field name="product_uom" string="UOM"/>
|
||||
<field name="location_id" string="Source Loc." />
|
||||
<field name="state" invisible="1"/>
|
||||
<button name="%(stock.action_partial_move)d"
|
||||
string="Partial"
|
||||
type="action" states="confirmed,assigned"
|
||||
<button name="%(stock.action_partial_move)d"
|
||||
string="Partial"
|
||||
type="action" states="confirmed,assigned"
|
||||
icon="gtk-justify-fill"/>
|
||||
<button name="%(stock.move_scrap)d"
|
||||
string="Scrap Products" type="action"
|
||||
@@ -729,8 +730,8 @@
|
||||
<field name="arch" type="xml">
|
||||
<search string="Search Production">
|
||||
<group col='9' colspan='4'>
|
||||
<filter icon="terp-document-new" string="Draft"
|
||||
domain="[('state','=','draft')]"
|
||||
<filter icon="terp-camera_test" string="Ready"
|
||||
domain="[('state','=','ready')]"
|
||||
help="Non confirmed manufacturing orders"/>
|
||||
<filter icon="terp-check" string="Current" name="current"
|
||||
default="1"
|
||||
@@ -935,9 +936,9 @@
|
||||
<menuitem action="mrp_workcenter_action" id="menu_view_resource_search_mrp" parent="menu_pm_resources_config" sequence="1"/>
|
||||
<menuitem action="resource.action_resource_calendar_form" id="menu_view_resource_calendar_search_mrp" parent="menu_pm_resources_config" sequence="1"/>
|
||||
<menuitem action="resource.action_resource_calendar_leave_tree" id="menu_view_resource_calendar_leaves_search_mrp" parent="menu_pm_resources_config" sequence="1"/>
|
||||
|
||||
|
||||
<!-- Planning -->
|
||||
|
||||
|
||||
<menuitem id="menu_mrp_planning" name="Planning"
|
||||
parent="base.menu_mrp_root" sequence="2"
|
||||
groups="base.group_extended"/>
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<!--
|
||||
Procurement
|
||||
-->
|
||||
|
||||
|
||||
|
||||
<record id="procurement_tree_view" model="ir.ui.view">
|
||||
<field name="name">procurement.order.tree</field>
|
||||
@@ -145,7 +145,7 @@
|
||||
<field name="search_view_id" ref="view_procurement_filter"/>
|
||||
<field name="help">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)</field>
|
||||
</record>
|
||||
|
||||
|
||||
<record id="procurement_action5" model="ir.actions.act_window">
|
||||
<field name="name">Procurement Exceptions</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
@@ -224,6 +224,10 @@
|
||||
<field name="procurement_id" readonly="1"/>
|
||||
<field name="active" />
|
||||
</group>
|
||||
<group col="4" colspan="2" groups="base.group_extended">
|
||||
<separator string="Procurement Orders to Process" colspan="2" />
|
||||
<field name="procurement_draft_ids" colspan="2" nolabel="1"/>
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
@@ -238,11 +242,11 @@
|
||||
<field name="help">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.</field>
|
||||
</record>
|
||||
|
||||
<act_window
|
||||
<act_window
|
||||
context="{'search_default_warehouse_id': active_id}"
|
||||
id="act_stock_warehouse_2_stock_warehouse_orderpoint"
|
||||
name="Minimum Stock Rules"
|
||||
res_model="stock.warehouse.orderpoint"
|
||||
name="Minimum Stock Rules"
|
||||
res_model="stock.warehouse.orderpoint"
|
||||
src_model="stock.warehouse"/>
|
||||
|
||||
<!-- add product_uom to context to be the default value when adding new orderpoints -->
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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']),
|
||||
]
|
||||
|
||||
|
||||
@@ -8,6 +8,18 @@
|
||||
<menuitem id="menu_procurement_management" name="Purchase Management"
|
||||
parent="base.menu_purchase_root" sequence="1" />
|
||||
|
||||
<menuitem id="menu_purhase_config_purchase" name="Configuration"
|
||||
parent="base.menu_purchase_root" sequence="100"/>
|
||||
|
||||
<menuitem
|
||||
action="product.product_pricelist_action" id="menu_product_pricelist_action_purhase"
|
||||
parent="menu_purhase_config_purchase" sequence="2"/>
|
||||
|
||||
<menuitem
|
||||
action="product.product_pricelist_action2" id="menu_product_pricelist_action2_purchase"
|
||||
parent="menu_purhase_config_purchase" sequence="1"/>
|
||||
|
||||
|
||||
<!--supplier addresses action-->
|
||||
<record id="action_supplier_address_form" model="ir.actions.act_window">
|
||||
<field name="name">Addresses</field>
|
||||
@@ -162,7 +174,7 @@
|
||||
<group colspan="2" col="2">
|
||||
<separator string="Invoice Control" colspan="2"/>
|
||||
<field name="invoice_method"/>
|
||||
<field name="invoice_id" readonly="1" groups="base.group_extended"/>
|
||||
<field name="invoice_id" readonly="1" groups="base.group_extended" />
|
||||
<field name="fiscal_position" widget="selection"/>
|
||||
</group>
|
||||
<newline/>
|
||||
@@ -312,12 +324,12 @@
|
||||
<field name="product_id"/>
|
||||
<field name="price_unit"/>
|
||||
<field name="product_qty"/>
|
||||
<field name="product_uom"/>
|
||||
<field name="product_uom"/>
|
||||
<field name="price_subtotal"/>
|
||||
<field name="date_planned" widget="date" width="135"/>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<field name="state" invisible="1"/>
|
||||
<field name="invoiced" invisible="1"/>
|
||||
</tree>
|
||||
@@ -375,7 +387,7 @@
|
||||
<field name="order_id"/>
|
||||
<field name="product_id"/>
|
||||
<field name="partner_id" string="Supplier"/>
|
||||
</group>
|
||||
</group>
|
||||
<newline/>
|
||||
<group expand="0" string="Group By...">
|
||||
<filter string="Supplier" icon="terp-partner" domain="[]" context="{'group_by' : 'partner_id'}" />
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
|
||||
#
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ class WebKitHelper(object):
|
||||
height = 'width="%spx"'%(height)
|
||||
else :
|
||||
height = ' '
|
||||
toreturn = '<img %s %s src="data:image/%s;base64,%s">'%(
|
||||
toreturn = '<img %s %s src="data:image/%s;base64,%s" />'%(
|
||||
width,
|
||||
height,
|
||||
type,
|
||||
|
||||
@@ -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])
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -102,9 +102,12 @@
|
||||
<field name="inherit_id" ref="product.product_normal_form_view"/>
|
||||
<field name="arch" type="xml">
|
||||
<group name="status" position="after">
|
||||
<group colspan="2" col="2">
|
||||
<group colspan="2" col="3">
|
||||
<separator string="Stocks" colspan="4"/>
|
||||
<field name="qty_available"/>
|
||||
<button name="%(action_view_change_product_quantity)d" string="Update" attrs="{'invisible': [('type', '=', 'service')]}"
|
||||
type="action" icon="gtk-execute" groups = "stock.group_stock_manager,product.group_product_manager"/>
|
||||
<newline/>
|
||||
<field name="virtual_available"/>
|
||||
</group>
|
||||
<group colspan="2" col="2" name="lot" groups="base.group_extended">
|
||||
|
||||
+20
-7
@@ -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
|
||||
|
||||
+17
-17
@@ -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"/>
|
||||
<button name="%(split_into)d" string="Put in a new pack" type="action"
|
||||
groups="base.group_extended"
|
||||
icon="terp-stock_effects-object-colorize"
|
||||
states="draft,assigned,confirmed,done"/>
|
||||
states="draft,assigned,confirmed"/>
|
||||
<button name="%(split_into)d" string="Put in a new pack" type="action"
|
||||
groups="base.group_extended"
|
||||
icon="terp-stock_effects-object-colorize"
|
||||
states="draft,assigned,confirmed,done"/>
|
||||
states="draft,assigned,confirmed"/>
|
||||
<field name="location_id"/>
|
||||
<field name="location_dest_id"/>
|
||||
<field name="date_expected" string="Date Expected"/>
|
||||
@@ -719,14 +719,14 @@
|
||||
<button name="%(split_into)d" string="New pack" type="action"
|
||||
groups="base.group_extended"
|
||||
icon="terp-stock_effects-object-colorize"
|
||||
states="draft,assigned,confirmed,done" colspan="1"/>
|
||||
states="draft,assigned,confirmed" colspan="1"/>
|
||||
<field name="prodlot_id" groups="base.group_extended"
|
||||
context="{'location_id':location_id, 'product_id':product_id}"
|
||||
domain="[('product_id','=?',product_id)]"
|
||||
on_change="onchange_lot_id(prodlot_id,product_qty, location_id, product_id)" colspan="3"/>
|
||||
<button name="%(track_line)d"
|
||||
groups="base.group_extended"
|
||||
states="draft,waiting,confirmed,assigned,done"
|
||||
states="draft,waiting,confirmed,assigned"
|
||||
string="Split" type="action" icon="terp-stock_effects-object-colorize" colspan="1" />
|
||||
</group>
|
||||
<label string="" colspan="4"/>
|
||||
@@ -859,18 +859,18 @@
|
||||
<field name="prodlot_id" groups="base.group_extended"/>
|
||||
<button name="%(track_line)d" string="Split in production lots" type="action"
|
||||
icon="terp-stock_effects-object-colorize" attrs="{'invisible': [('prodlot_id','<>',False)]}"
|
||||
states="draft,assigned,confirmed,done"
|
||||
states="draft,assigned,confirmed"
|
||||
groups="base.group_extended"/>
|
||||
<field name="tracking_id" groups="base.group_extended"/>
|
||||
<button name="setlast_tracking" string="Put in current pack" type="object"
|
||||
attrs="{'invisible': [('tracking_id','<>',False)]}"
|
||||
groups="base.group_extended"
|
||||
icon="terp-stock_effects-object-colorize"
|
||||
states="draft,assigned,confirmed,done"/>
|
||||
states="draft,assigned,confirmed"/>
|
||||
<button name="%(split_into)d" string="Put in a new pack" type="action"
|
||||
icon="terp-stock_effects-object-colorize"
|
||||
groups="base.group_extended"
|
||||
states="draft,assigned,confirmed,done"/>
|
||||
states="draft,assigned,confirmed"/>
|
||||
<field name="location_id"/>
|
||||
<field name="date"/>
|
||||
<field name="state"/>
|
||||
@@ -912,14 +912,14 @@
|
||||
<button name="%(split_into)d" string="New pack" type="action"
|
||||
groups="base.group_extended"
|
||||
icon="terp-stock_effects-object-colorize"
|
||||
states="draft,assigned,confirmed,done" colspan="1"/>
|
||||
states="draft,assigned,confirmed" colspan="1"/>
|
||||
<field name="prodlot_id" groups="base.group_extended"
|
||||
context="{'location_id':location_id, 'product_id':product_id}"
|
||||
domain="[('product_id','=?',product_id)]"
|
||||
on_change="onchange_lot_id(prodlot_id,product_qty, location_id, product_id)" colspan="3"/>
|
||||
<button name="%(track_line)d"
|
||||
groups="base.group_extended"
|
||||
states="draft,waiting,confirmed,assigned,done"
|
||||
states="draft,waiting,confirmed,assigned"
|
||||
string="Split" type="action" icon="terp-stock_effects-object-colorize" colspan="1" />
|
||||
</group>
|
||||
<label string="" colspan="4"/>
|
||||
@@ -1089,7 +1089,7 @@
|
||||
<button name="setlast_tracking" string="Put in current pack" type="object"
|
||||
groups="base.group_extended"
|
||||
icon="terp-stock_effects-object-colorize" attrs="{'invisible': [('tracking_id','<>',False)]}"
|
||||
states="draft,assigned,confirmed,done"/>
|
||||
states="draft,assigned,confirmed"/>
|
||||
<button name="%(split_into)d" string="Put in a new pack" type="action"
|
||||
groups="base.group_extended"
|
||||
icon="terp-stock_effects-object-colorize"
|
||||
@@ -1132,14 +1132,14 @@
|
||||
<button name="%(split_into)d" string="New pack" type="action"
|
||||
groups="base.group_extended"
|
||||
icon="terp-stock_effects-object-colorize"
|
||||
states="draft,assigned,confirmed,done" colspan="1"/>
|
||||
states="draft,assigned,confirmed" colspan="1"/>
|
||||
<field name="prodlot_id" groups="base.group_extended"
|
||||
context="{'location_id':location_id, 'product_id':product_id}"
|
||||
domain="[('product_id','=?',product_id)]"
|
||||
on_change="onchange_lot_id(prodlot_id,product_qty, location_id, product_id)" colspan="3"/>
|
||||
<button name="%(track_line)d"
|
||||
groups="base.group_extended"
|
||||
states="draft,waiting,confirmed,assigned,done"
|
||||
states="draft,waiting,confirmed,assigned"
|
||||
string="Split" type="action" icon="terp-stock_effects-object-colorize" colspan="1" />
|
||||
</group>
|
||||
<label string="" colspan="4"/>
|
||||
@@ -1399,14 +1399,14 @@
|
||||
<button name="%(split_into)d" string="New pack" type="action"
|
||||
groups="base.group_extended"
|
||||
icon="terp-stock_effects-object-colorize"
|
||||
states="draft,assigned,confirmed,done" colspan="1"/>
|
||||
states="draft,assigned,confirmed" colspan="1"/>
|
||||
<field name="prodlot_id" groups="base.group_extended"
|
||||
context="{'location_id':location_id, 'product_id':product_id}"
|
||||
domain="[('product_id','=?',product_id)]"
|
||||
on_change="onchange_lot_id(prodlot_id,product_qty, location_id, product_id)" colspan="3"/>
|
||||
<button name="%(track_line)d"
|
||||
groups="base.group_extended"
|
||||
states="draft,waiting,confirmed,assigned,done"
|
||||
states="draft,waiting,confirmed,assigned"
|
||||
string="Split" type="action" icon="terp-stock_effects-object-colorize" colspan="1"/>
|
||||
</group>
|
||||
|
||||
@@ -1758,7 +1758,7 @@
|
||||
src_model="stock.location"/>
|
||||
|
||||
<act_window
|
||||
context="{'location': active_id, 'search_default_done': 1,'search_default_product_id': [active_id]}"
|
||||
context="{'search_default_done': 1,'search_default_product_id': [active_id]}"
|
||||
id="act_product_stock_move_open"
|
||||
name="Stock Moves"
|
||||
res_model="stock.move"
|
||||
@@ -1772,7 +1772,7 @@
|
||||
src_model="stock.move"/>
|
||||
|
||||
<act_window
|
||||
context="{'location': active_id, 'search_default_future': 1,'search_default_product_id': [active_id]}"
|
||||
context="{'search_default_future': 1,'search_default_product_id': [active_id]}"
|
||||
domain="[('state','in',('waiting','confirmed','assigned'))]"
|
||||
id="act_product_stock_move_futur_open"
|
||||
name="Future Stock Moves"
|
||||
|
||||
@@ -33,6 +33,6 @@ import stock_invoice_onshipping
|
||||
import stock_location_product
|
||||
import stock_change_standard_price
|
||||
import stock_return_picking
|
||||
|
||||
import stock_change_product_qty
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
|
||||
#
|
||||
# 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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
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:
|
||||
@@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
<record id="view_change_product_quantity" model="ir.ui.view">
|
||||
<field name="name">Change Product Quantity</field>
|
||||
<field name="model">stock.change.product.qty</field>
|
||||
<field name="type">form</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Change Product Quantity">
|
||||
<separator string="Select Quantity" colspan="4"/>
|
||||
<newline/>
|
||||
<field name="new_quantity" />
|
||||
<newline/>
|
||||
<field name="warehouse_id" on_change="onchange_warehouse_id(warehouse_id)" widget="selection"/>
|
||||
<field name="location_id" />
|
||||
<separator string="" colspan="4" />
|
||||
<label string="" colspan="2"/>
|
||||
<group col="2" colspan="2">
|
||||
<button special="cancel" string="_Cancel" icon="gtk-cancel"/>
|
||||
<button name="change_product_qty" string="_Apply" type="object" icon="gtk-apply"/>
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_view_change_product_quantity" model="ir.actions.act_window">
|
||||
<field name="name">Change Product Quantity</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">stock.change.product.qty</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="view_id" ref="view_change_product_quantity"/>
|
||||
<field name="target">new</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
|
||||
@@ -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('<field name="return%s"/>\n<newline/>' % (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('<field name="invoice_state"/>\n<newline/>')
|
||||
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,
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
|
||||
#
|
||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user