[WIP] split stock module in stock and stock_account that installs itself only if account+stock are installed
bzr revid: qdp-launchpad@openerp.com-20130711130528-c9jjiduqriakj9iv
This commit is contained in:
parent
01b065a9e8
commit
038fd04ea5
|
@ -24,12 +24,12 @@
|
|||
'version': '1.1',
|
||||
'author': 'OpenERP SA',
|
||||
'summary': 'Inventory, Logistic, Storage',
|
||||
'description' : """
|
||||
'description': """
|
||||
Manage multi-warehouses, multi- and structured stock locations
|
||||
==============================================================
|
||||
|
||||
The warehouse and inventory management is based on a hierarchical location structure, from warehouses to storage bins.
|
||||
The double entry inventory system allows you to manage customers, suppliers as well as manufacturing inventories.
|
||||
The warehouse and inventory management is based on a hierarchical location structure, from warehouses to storage bins.
|
||||
The double entry inventory system allows you to manage customers, suppliers as well as manufacturing inventories.
|
||||
|
||||
OpenERP has the capacity to manage lots and serial numbers ensuring compliance with the traceability requirements imposed by the majority of industries.
|
||||
|
||||
|
@ -53,8 +53,8 @@ Dashboard / Reports for Warehouse Management will include:
|
|||
* Moves Analysis
|
||||
""",
|
||||
'website': 'http://www.openerp.com',
|
||||
'images': ['images/stock_forecast_report.png', 'images/delivery_orders.jpeg', 'images/inventory_analysis.jpeg','images/location.jpeg','images/moves_analysis.jpeg','images/physical_inventories.jpeg','images/warehouse_dashboard.jpeg'],
|
||||
'depends': ['product', 'account','procurement'],
|
||||
'images': ['images/stock_forecast_report.png', 'images/delivery_orders.jpeg', 'images/inventory_analysis.jpeg', 'images/location.jpeg', 'images/moves_analysis.jpeg', 'images/physical_inventories.jpeg', 'images/warehouse_dashboard.jpeg'],
|
||||
'depends': ['product', 'procurement', 'board'],
|
||||
'category': 'Warehouse Management',
|
||||
'sequence': 16,
|
||||
'demo': [
|
||||
|
@ -72,12 +72,10 @@ Dashboard / Reports for Warehouse Management will include:
|
|||
'wizard/stock_partial_picking_view.xml',
|
||||
'wizard/stock_partial_move_view.xml',
|
||||
'wizard/stock_fill_inventory_view.xml',
|
||||
'wizard/stock_invoice_onshipping_view.xml',
|
||||
'wizard/stock_inventory_merge_view.xml',
|
||||
'wizard/stock_location_product_view.xml',
|
||||
'wizard/stock_splitinto_view.xml',
|
||||
'wizard/stock_inventory_line_split_view.xml',
|
||||
'wizard/stock_change_standard_price_view.xml',
|
||||
'wizard/stock_return_picking_view.xml',
|
||||
'wizard/make_procurement_view.xml',
|
||||
'wizard/mrp_procurement_view.xml',
|
||||
|
@ -87,7 +85,6 @@ Dashboard / Reports for Warehouse Management will include:
|
|||
'stock_report.xml',
|
||||
'stock_view.xml',
|
||||
'stock_sequence.xml',
|
||||
'product_data.xml',
|
||||
'product_view.xml',
|
||||
'partner_view.xml',
|
||||
'report/report_stock_move_view.xml',
|
||||
|
@ -103,15 +100,15 @@ Dashboard / Reports for Warehouse Management will include:
|
|||
'installable': True,
|
||||
'application': True,
|
||||
'auto_install': False,
|
||||
'css': [
|
||||
'css': [
|
||||
'static/src/css/picking.css',
|
||||
'static/src/css/stock.css',
|
||||
'static/src/css/stock.css',
|
||||
],
|
||||
'js': [
|
||||
'static/src/js/widgets.js',
|
||||
'static/src/js/main.js',
|
||||
],
|
||||
'qweb':['static/src/xml/picking.xml'],
|
||||
'qweb': ['static/src/xml/picking.xml'],
|
||||
}
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
|
|
@ -36,3 +36,4 @@ class res_partner(osv.osv):
|
|||
help="This stock location will be used, instead of the default one, as the source location for goods you receive from the current partner"),
|
||||
}
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
|
|
@ -47,147 +47,6 @@ class product_product(osv.osv):
|
|||
res[product_id]['delivery_count'] = move['product_id_count']
|
||||
return res
|
||||
|
||||
def get_product_accounts(self, cr, uid, product_id, context=None):
|
||||
""" To get the stock input account, stock output account and stock journal related to product.
|
||||
@param product_id: product id
|
||||
@return: dictionary which contains information regarding stock input account, stock output account and stock journal
|
||||
"""
|
||||
if context is None:
|
||||
context = {}
|
||||
product_obj = self.pool.get('product.product').browse(cr, uid, product_id, context=context)
|
||||
|
||||
stock_input_acc = product_obj.property_stock_account_input and product_obj.property_stock_account_input.id or False
|
||||
if not stock_input_acc:
|
||||
stock_input_acc = product_obj.categ_id.property_stock_account_input_categ and product_obj.categ_id.property_stock_account_input_categ.id or False
|
||||
|
||||
stock_output_acc = product_obj.property_stock_account_output and product_obj.property_stock_account_output.id or False
|
||||
if not stock_output_acc:
|
||||
stock_output_acc = product_obj.categ_id.property_stock_account_output_categ and product_obj.categ_id.property_stock_account_output_categ.id or False
|
||||
|
||||
journal_id = product_obj.categ_id.property_stock_journal and product_obj.categ_id.property_stock_journal.id or False
|
||||
account_valuation = product_obj.categ_id.property_stock_valuation_account_id and product_obj.categ_id.property_stock_valuation_account_id.id or False
|
||||
return {
|
||||
'stock_account_input': stock_input_acc,
|
||||
'stock_account_output': stock_output_acc,
|
||||
'stock_journal': journal_id,
|
||||
'property_stock_valuation_account_id': account_valuation
|
||||
}
|
||||
|
||||
# FP Note:;too complex, not good, should be implemented at quant level TODO
|
||||
def do_change_standard_price(self, cr, uid, ids, datas, context=None):
|
||||
""" Changes the Standard Price of Product and creates an account move accordingly.
|
||||
@param datas : dict. contain default datas like new_price, stock_output_account, stock_input_account, stock_journal
|
||||
@param context: A standard dictionary
|
||||
@return:
|
||||
|
||||
"""
|
||||
location_obj = self.pool.get('stock.location')
|
||||
move_obj = self.pool.get('account.move')
|
||||
move_line_obj = self.pool.get('account.move.line')
|
||||
if context is None:
|
||||
context = {}
|
||||
|
||||
new_price = datas.get('new_price', 0.0)
|
||||
stock_output_acc = datas.get('stock_output_account', False)
|
||||
stock_input_acc = datas.get('stock_input_account', False)
|
||||
journal_id = datas.get('stock_journal', False)
|
||||
product_obj=self.browse(cr, uid, ids, context=context)[0]
|
||||
account_valuation = product_obj.categ_id.property_stock_valuation_account_id
|
||||
account_valuation_id = account_valuation and account_valuation.id or False
|
||||
if not account_valuation_id: raise osv.except_osv(_('Error!'), _('Specify valuation Account for Product Category: %s.') % (product_obj.categ_id.name))
|
||||
move_ids = []
|
||||
loc_ids = location_obj.search(cr, uid,[('usage','=','internal')])
|
||||
for rec_id in ids:
|
||||
for location in location_obj.browse(cr, uid, loc_ids, context=context):
|
||||
c = context.copy()
|
||||
c.update({
|
||||
'location': location.id,
|
||||
'compute_child': False
|
||||
})
|
||||
|
||||
product = self.browse(cr, uid, rec_id, context=c)
|
||||
qty = product.qty_available
|
||||
diff = product.standard_price - new_price
|
||||
if not diff: raise osv.except_osv(_('Error!'), _("No difference between standard price and new price!"))
|
||||
if qty:
|
||||
company_id = location.company_id and location.company_id.id or False
|
||||
if not company_id: raise osv.except_osv(_('Error!'), _('Please specify company in Location.'))
|
||||
#
|
||||
# Accounting Entries
|
||||
#
|
||||
if not journal_id:
|
||||
journal_id = product.categ_id.property_stock_journal and product.categ_id.property_stock_journal.id or False
|
||||
if not journal_id:
|
||||
raise osv.except_osv(_('Error!'),
|
||||
_('Please define journal '\
|
||||
'on the product category: "%s" (id: %d).') % \
|
||||
(product.categ_id.name,
|
||||
product.categ_id.id,))
|
||||
move_id = move_obj.create(cr, uid, {
|
||||
'journal_id': journal_id,
|
||||
'company_id': company_id
|
||||
})
|
||||
|
||||
move_ids.append(move_id)
|
||||
|
||||
|
||||
if diff > 0:
|
||||
if not stock_input_acc:
|
||||
stock_input_acc = product.\
|
||||
property_stock_account_input.id
|
||||
if not stock_input_acc:
|
||||
stock_input_acc = product.categ_id.\
|
||||
property_stock_account_input_categ.id
|
||||
if not stock_input_acc:
|
||||
raise osv.except_osv(_('Error!'),
|
||||
_('Please define stock input account ' \
|
||||
'for this product: "%s" (id: %d).') % \
|
||||
(product.name,
|
||||
product.id,))
|
||||
amount_diff = qty * diff
|
||||
move_line_obj.create(cr, uid, {
|
||||
'name': product.name,
|
||||
'account_id': stock_input_acc,
|
||||
'debit': amount_diff,
|
||||
'move_id': move_id,
|
||||
})
|
||||
move_line_obj.create(cr, uid, {
|
||||
'name': product.categ_id.name,
|
||||
'account_id': account_valuation_id,
|
||||
'credit': amount_diff,
|
||||
'move_id': move_id
|
||||
})
|
||||
elif diff < 0:
|
||||
if not stock_output_acc:
|
||||
stock_output_acc = product.\
|
||||
property_stock_account_output.id
|
||||
if not stock_output_acc:
|
||||
stock_output_acc = product.categ_id.\
|
||||
property_stock_account_output_categ.id
|
||||
if not stock_output_acc:
|
||||
raise osv.except_osv(_('Error!'),
|
||||
_('Please define stock output account ' \
|
||||
'for this product: "%s" (id: %d).') % \
|
||||
(product.name,
|
||||
product.id,))
|
||||
amount_diff = qty * -diff
|
||||
move_line_obj.create(cr, uid, {
|
||||
'name': product.name,
|
||||
'account_id': stock_output_acc,
|
||||
'credit': amount_diff,
|
||||
'move_id': move_id
|
||||
})
|
||||
move_line_obj.create(cr, uid, {
|
||||
'name': product.categ_id.name,
|
||||
'account_id': account_valuation_id,
|
||||
'debit': amount_diff,
|
||||
'move_id': move_id
|
||||
})
|
||||
|
||||
self.write(cr, uid, rec_id, {'standard_price': new_price})
|
||||
|
||||
return move_ids
|
||||
|
||||
def view_header_get(self, cr, user, view_id, view_type, context=None):
|
||||
if context is None:
|
||||
context = {}
|
||||
|
@ -343,15 +202,6 @@ class product_product(osv.osv):
|
|||
'track_outgoing': fields.boolean('Track Outgoing Lots', help="Forces to specify a Serial Number for all moves containing this product and going to a Customer Location"),
|
||||
'location_id': fields.dummy(string='Location', relation='stock.location', type='many2one'),
|
||||
'warehouse_id': fields.dummy(string='Warehouse', relation='stock.warehouse', type='many2one'),
|
||||
'valuation':fields.property(type='selection', selection=[('manual_periodic', 'Periodical (manual)'),
|
||||
('real_time','Real Time (automated)'),], string = 'Inventory Valuation',
|
||||
help="If real-time valuation is enabled for a product, the system will automatically write journal entries corresponding to stock moves." \
|
||||
"The inventory variation account set on the product category will represent the current inventory value, and the stock input and stock output account will hold the counterpart moves for incoming and outgoing products."
|
||||
, required=True),
|
||||
}
|
||||
|
||||
_defaults = {
|
||||
'valuation': 'manual_periodic',
|
||||
}
|
||||
|
||||
def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
|
||||
|
@ -420,18 +270,6 @@ class product_template(osv.osv):
|
|||
string="Inventory Location",
|
||||
domain=[('usage','like','inventory')],
|
||||
help="This stock location will be used, instead of the default one, as the source location for stock moves generated when you do an inventory."),
|
||||
'property_stock_account_input': fields.property(
|
||||
type='many2one',
|
||||
relation='account.account',
|
||||
string='Stock Input Account',
|
||||
help="When doing real-time inventory valuation, counterpart journal items for all incoming stock moves will be posted in this account, unless "
|
||||
"there is a specific valuation account set on the source location. When not set on the product, the one from the product category is used."),
|
||||
'property_stock_account_output': fields.property(
|
||||
type='many2one',
|
||||
relation='account.account',
|
||||
string='Stock Output Account',
|
||||
help="When doing real-time inventory valuation, counterpart journal items for all outgoing stock moves will be posted in this account, unless "
|
||||
"there is a specific valuation account set on the destination location. When not set on the product, the one from the product category is used."),
|
||||
'sale_delay': fields.float('Customer Lead Time', help="The average delay in days between the confirmation of the customer order and the delivery of the finished products. It's the time you promise to your customers."),
|
||||
'loc_rack': fields.char('Rack', size=16),
|
||||
'loc_row': fields.char('Row', size=16),
|
||||
|
@ -443,35 +281,4 @@ class product_template(osv.osv):
|
|||
}
|
||||
|
||||
|
||||
|
||||
class product_category(osv.osv):
|
||||
_inherit = 'product.category'
|
||||
_columns = {
|
||||
'property_stock_journal': fields.property(
|
||||
relation='account.journal',
|
||||
type='many2one',
|
||||
string='Stock Journal',
|
||||
help="When doing real-time inventory valuation, this is the Accounting Journal in which entries will be automatically posted when stock moves are processed."),
|
||||
'property_stock_account_input_categ': fields.property(
|
||||
type='many2one',
|
||||
relation='account.account',
|
||||
string='Stock Input Account',
|
||||
help="When doing real-time inventory valuation, counterpart journal items for all incoming stock moves will be posted in this account, unless "
|
||||
"there is a specific valuation account set on the source location. This is the default value for all products in this category. It "
|
||||
"can also directly be set on each product"),
|
||||
'property_stock_account_output_categ': fields.property(
|
||||
type='many2one',
|
||||
relation='account.account',
|
||||
string='Stock Output Account',
|
||||
help="When doing real-time inventory valuation, counterpart journal items for all outgoing stock moves will be posted in this account, unless "
|
||||
"there is a specific valuation account set on the destination location. This is the default value for all products in this category. It "
|
||||
"can also directly be set on each product"),
|
||||
'property_stock_valuation_account_id': fields.property(
|
||||
type='many2one',
|
||||
relation='account.account',
|
||||
string="Stock Valuation Account",
|
||||
help="When real-time inventory valuation is enabled on a product, this account will hold the current value of the products.",),
|
||||
}
|
||||
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
|
|
@ -14,24 +14,6 @@
|
|||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_category_property_form" model="ir.ui.view">
|
||||
<field name="name">product.category.stock.property.form.inherit</field>
|
||||
<field name="model">product.category</field>
|
||||
<field name="inherit_id" ref="account.view_category_property_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<data>
|
||||
<group name="account_property" position="after">
|
||||
<group name="account_stock_property" string="Account Stock Properties" colspan="2">
|
||||
<field name="property_stock_account_input_categ" domain="[('type','<>','view'),('type','<>','consolidation')]"/>
|
||||
<field name="property_stock_account_output_categ" domain="[('type','<>','view'),('type','<>','consolidation')]"/>
|
||||
<field name="property_stock_valuation_account_id" domain="[('type','<>','view'), ('type','<>','consolidation')]"/>
|
||||
<field name="property_stock_journal"/>
|
||||
</group>
|
||||
</group>
|
||||
</data>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_template_property_form" model="ir.ui.view">
|
||||
<field name="name">product.template.stock.property.form.inherit</field>
|
||||
<field name="model">product.template</field>
|
||||
|
@ -48,16 +30,12 @@
|
|||
</group>
|
||||
</group>
|
||||
<page position="after" string="Information">
|
||||
<page string="Properties">
|
||||
<page string="Properties" name="properties">
|
||||
<group string="Counter-Part Locations Properties" groups="stock.group_locations">
|
||||
<field name="property_stock_procurement" domain="[('usage','=','procurement')]"/>
|
||||
<field name="property_stock_production" domain="[('usage','=','production')]"/>
|
||||
<field name="property_stock_inventory" domain="[('usage','=','inventory')]"/>
|
||||
</group>
|
||||
<group string="Accounting Entries">
|
||||
<field name="property_stock_account_input" domain="[('type','<>','view'),('type','<>','consolidation')]"/>
|
||||
<field name="property_stock_account_output" domain="[('type','<>','view'),('type','<>','consolidation')]"/>
|
||||
</group>
|
||||
</page>
|
||||
</page>
|
||||
</field>
|
||||
|
@ -131,48 +109,6 @@
|
|||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_product_standard_price_form" model="ir.ui.view">
|
||||
<field name="name">product.product.standard.price.form.inherit</field>
|
||||
<field name="model">product.product</field>
|
||||
<field name="inherit_id" ref="product.product_normal_form_view"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="standard_price" position="replace" version="7.0">
|
||||
<label string="Cost Price" for="standard_price" align="1.0" groups="base.group_user"/>
|
||||
<div groups="base.group_user">
|
||||
<field name="standard_price" attrs="{'readonly':[('valuation','=','real_time'), ('cost_method', 'in', ['standard', 'average'])]}" nolabel="1"/>
|
||||
<button name="%(action_view_change_standard_price)d" string="- update"
|
||||
type="action" attrs="{'invisible':['|', ('valuation','!=', 'real_time'), ('cost_method', 'not in', ['standard', 'average'])]}"
|
||||
class="oe_link" groups="product.group_costing_method"/>
|
||||
</div>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_normal_property_acc_form" model="ir.ui.view">
|
||||
<field name="name">product.normal.stock.acc.property.form.inherit</field>
|
||||
<field name="model">product.product</field>
|
||||
<field name="inherit_id" ref="product.product_normal_form_view"/>
|
||||
<field name="priority">26</field>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//group[@name='properties']" position="before">
|
||||
<group groups="stock.group_inventory_valuation">
|
||||
<separator string="Inventory Valuation" colspan="4"/>
|
||||
<group colspan="2" col="2">
|
||||
<field name="valuation" attrs="{'readonly':[('type', '=', 'service')]}"/>
|
||||
</group>
|
||||
<group colspan="2" col="2">
|
||||
<field name="property_stock_account_input" attrs="{'invisible':[('valuation', '!=', 'real_time')]}"
|
||||
domain="[('type','<>','view'),('type','<>','consolidation')]"/>
|
||||
<field name="property_stock_account_output" attrs="{'invisible':[('valuation', '!=', 'real_time')]}"
|
||||
domain="[('type','<>','view'),('type','<>','consolidation')]"/>
|
||||
</group>
|
||||
</group>
|
||||
<newline/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
<record id="action_receive_move" model="ir.actions.act_window">
|
||||
<field name="name">Receptions</field>
|
||||
<field name="res_model">stock.move</field>
|
||||
|
|
|
@ -62,7 +62,7 @@ This installs the module product_expiry."""),
|
|||
'group_stock_tracking_lot': fields.boolean("Track serial number on logistic units (pallets)",
|
||||
implied_group='stock.group_tracking_lot',
|
||||
help="""When you select a serial number on product moves, you can get the upstream or downstream traceability of that product."""),
|
||||
'group_stock_inventory_valuation': fields.boolean("Generate accounting entries per stock movement",
|
||||
'module_stock_account': fields.boolean("Generate accounting entries per stock movement",
|
||||
implied_group='stock.group_inventory_valuation',
|
||||
help="""Allows to configure inventory valuations on products and product categories."""),
|
||||
'group_stock_multiple_locations': fields.boolean("Manage multiple locations and warehouses",
|
||||
|
|
|
@ -45,8 +45,8 @@
|
|||
<label for="id" string="Accounting"/>
|
||||
<div>
|
||||
<div>
|
||||
<field name="group_stock_inventory_valuation" class="oe_inline"/>
|
||||
<label for="group_stock_inventory_valuation"/>
|
||||
<field name="module_stock_account" class="oe_inline"/>
|
||||
<label for="module_stock_account"/>
|
||||
</div>
|
||||
<div>
|
||||
<field name="module_stock_invoice_directly" class="oe_inline"/>
|
||||
|
|
|
@ -29,11 +29,6 @@ access_stock_lines_date_manager,report.stock.lines.date manager,model_report_sto
|
|||
access_report_stock_inventory_user,report.stock.inventory user,model_report_stock_inventory,stock.group_stock_user,1,1,1,1
|
||||
access_product_product_stock_user,product_product_stock_user,product.model_product_product,stock.group_stock_user,1,1,0,0
|
||||
access_product_template_stock_user,product.template stock user,product.model_product_template,stock.group_stock_user,1,1,0,0
|
||||
access_account_invoice_user,account.invoice stock user,account.model_account_invoice,stock.group_stock_user,1,1,1,0
|
||||
access_account_invoice_line_user,account.invoice.line stock user,account.model_account_invoice_line,stock.group_stock_user,1,1,1,0
|
||||
access_account_invoice_tax_user,account.invoice.tax stock user,account.model_account_invoice_tax,stock.group_stock_user,1,1,1,0
|
||||
access_account_journal_user,account.journal stock user,account.model_account_journal,stock.group_stock_user,1,0,0,0
|
||||
access_account_fiscalyear,account.fiscalyear stock user,account.model_account_fiscalyear,stock.group_stock_user,1,0,0,0
|
||||
access_product_pricelist_item_sale_manager,product.pricelist.item salemanager,product.model_product_pricelist_item,base.group_sale_manager,1,1,1,1
|
||||
access_product_uom_categ_stock_manager,product.uom.categ stock_manager,product.model_product_uom_categ,stock.group_stock_manager,1,1,1,1
|
||||
access_product_uom_stock_manager,product.uom stock_manager,product.model_product_uom,stock.group_stock_manager,1,1,1,1
|
||||
|
@ -51,7 +46,6 @@ access_ir_property_group_stock_manager,ir_property group_stock_manager,base.mode
|
|||
access_product_group_res_partner_stock_manager,res_partner group_stock_manager,base.model_res_partner,stock.group_stock_manager,1,1,1,0
|
||||
access_product_pricelist_version_stock_manager,product.pricelist.version stock_manager,product.model_product_pricelist_version,stock.group_stock_manager,1,1,1,1
|
||||
access_product_pricelist_item_stock_manager,product.pricelist.item stock_manager,product.model_product_pricelist_item,stock.group_stock_manager,1,1,1,1
|
||||
access_account_account_stock_manager,account.account stock manager,account.model_account_account,stock.group_stock_manager,1,0,0,0
|
||||
access_board_stock_user,board.board user,board.model_board_board,stock.group_stock_user,1,1,0,0
|
||||
access_stock_warehouse_orderpoint,stock.warehouse.orderpoint,model_stock_warehouse_orderpoint,stock.group_stock_user,1,0,0,0
|
||||
access_stock_warehouse_orderpoint_system,stock.warehouse.orderpoint system,model_stock_warehouse_orderpoint,stock.group_stock_manager,1,1,1,1
|
||||
|
|
|
|
@ -24,11 +24,6 @@
|
|||
<field name="category_id" ref="base.module_category_hidden"/>
|
||||
</record>
|
||||
|
||||
<record id="group_inventory_valuation" model="res.groups">
|
||||
<field name="name">Manage Inventory valuation</field>
|
||||
<field name="category_id" ref="base.module_category_hidden"/>
|
||||
</record>
|
||||
|
||||
<record id="group_locations" model="res.groups">
|
||||
<field name="name">Manage Multiple Locations and Warehouses</field>
|
||||
<field name="category_id" ref="base.module_category_hidden"/>
|
||||
|
|
|
@ -113,7 +113,7 @@ class stock_location(osv.osv):
|
|||
'partner_id': fields.many2one('res.partner', 'Owner',help="Owner of the location if not internal"),
|
||||
|
||||
'comment': fields.text('Additional Information'),
|
||||
'posx': fields.integer('Corridor (X)',help="Optional localization details, for information purpose only"),
|
||||
'posx': fields.integer('Corridor (X)', help="Optional localization details, for information purpose only"),
|
||||
'posy': fields.integer('Shelves (Y)', help="Optional localization details, for information purpose only"),
|
||||
'posz': fields.integer('Height (Z)', help="Optional localization details, for information purpose only"),
|
||||
|
||||
|
@ -122,16 +122,6 @@ class stock_location(osv.osv):
|
|||
|
||||
'company_id': fields.many2one('res.company', 'Company', select=1, help='Let this field empty if this location is shared between all companies'),
|
||||
'scrap_location': fields.boolean('Scrap Location', help='Check this box to allow using this location to put scrapped/damaged goods.'),
|
||||
'valuation_in_account_id': fields.many2one('account.account', 'Stock Valuation Account (Incoming)', domain = [('type','=','other')],
|
||||
help="Used for real-time inventory valuation. When set on a virtual location (non internal type), "
|
||||
"this account will be used to hold the value of products being moved from an internal location "
|
||||
"into this location, instead of the generic Stock Output Account set on the product. "
|
||||
"This has no effect for internal locations."),
|
||||
'valuation_out_account_id': fields.many2one('account.account', 'Stock Valuation Account (Outgoing)', domain = [('type','=','other')],
|
||||
help="Used for real-time inventory valuation. When set on a virtual location (non internal type), "
|
||||
"this account will be used to hold the value of products being moved out of this location "
|
||||
"and into an internal location, instead of the generic Stock Output Account set on the product. "
|
||||
"This has no effect for internal locations."),
|
||||
}
|
||||
_defaults = {
|
||||
'active': True,
|
||||
|
@ -190,24 +180,26 @@ class stock_quant(osv.osv):
|
|||
|
||||
# add location_dest_id in parameters (False=use the desitnation of the move)
|
||||
def quants_move(self, cr, uid, quants, move, context=None):
|
||||
for quant,qty in quants:
|
||||
location_from = quant and quant.location_id
|
||||
if not quant:
|
||||
quant = self._quant_create(cr, uid, qty, move, context=context)
|
||||
else:
|
||||
self._quant_split(cr, uid, quant, qty, context=context)
|
||||
self._quant_reconcile_negative(cr, uid, quant, context=context)
|
||||
for quant, qty in quants:
|
||||
self.move_single_quant(cr, uid, quant, qty, move, context=context)
|
||||
|
||||
# FP Note: improve this using preferred locations
|
||||
location_to = move.location_dest_id
|
||||
def move_single_quant(self, cr, uid, quant, qty, move, context=None):
|
||||
location_from = quant and quant.location_id
|
||||
if not quant:
|
||||
quant = self._quant_create(cr, uid, qty, move, context=context)
|
||||
else:
|
||||
self._quant_split(cr, uid, quant, qty, context=context)
|
||||
self._quant_reconcile_negative(cr, uid, quant, context=context)
|
||||
|
||||
self.write(cr, uid, [quant.id], {
|
||||
'location_id': location_to.id,
|
||||
'reservation_id': move.move_dest_id and move.move_dest_id.id or False,
|
||||
'history_ids': [(4, move.id)]
|
||||
})
|
||||
# FP Note: improve this using preferred locations
|
||||
location_to = move.location_dest_id
|
||||
|
||||
self.write(cr, uid, [quant.id], {
|
||||
'location_id': location_to.id,
|
||||
'reservation_id': move.move_dest_id and move.move_dest_id.id or False,
|
||||
'history_ids': [(4, move.id)]
|
||||
})
|
||||
|
||||
self._account_entry_move(cr, uid, quant, location_from, location_to, move, context=context)
|
||||
|
||||
# FP Note: TODO: implement domain preference that tries to retrieve first with this domain
|
||||
# This will be used for reservation
|
||||
|
@ -312,10 +304,8 @@ class stock_quant(osv.osv):
|
|||
self._price_update(cr, uid, qu2, cost, context=context)
|
||||
return result
|
||||
|
||||
# FP Note: this is where we should post accounting entries for adjustment
|
||||
def _price_update(self, cr, uid, quant, newprice, context=None):
|
||||
self.write(cr, uid, [quant.id], {'cost': newprice}, context=context)
|
||||
# TODO: generate accounting entries
|
||||
|
||||
#
|
||||
# Implementation of removal strategies
|
||||
|
@ -351,235 +341,10 @@ class stock_quant(osv.osv):
|
|||
return self._quants_get_order(cr, uid, location, product, quantity,
|
||||
domain, 'in_date desc', context=context)
|
||||
|
||||
"""
|
||||
Accounting Valuation Entries
|
||||
|
||||
location_from: can be None if it's a new quant
|
||||
"""
|
||||
def _account_entry_move(self, cr, uid, quant, location_from, location_to, move, context=None):
|
||||
if quant.product_id.valuation <> 'realtime':
|
||||
return False
|
||||
company_from = self._location_owner(cr, uid, quant, location_from, context=context)
|
||||
company_to = self._location_owner(cr, uid, quant, location_to, context=context)
|
||||
if company_from == company_to:
|
||||
return False
|
||||
|
||||
# Create Journal Entry for products arriving in the company
|
||||
if company_to:
|
||||
pass
|
||||
|
||||
# Create Journal Entry for products leaving the company
|
||||
if company_from:
|
||||
pass
|
||||
|
||||
|
||||
# Return the company owning the location if any
|
||||
def _location_owner(self, cr, uid, quant, location, context=None):
|
||||
return location and (location.usage == 'internal') and location.company_id or False
|
||||
|
||||
# TODO: move this code on the _account_entry_move method above. Should be simpler
|
||||
#
|
||||
#def _get_accounting_data_for_valuation(self, cr, uid, move, context=None):
|
||||
# """
|
||||
# Return the accounts and journal to use to post Journal Entries for the real-time
|
||||
# valuation of the move.
|
||||
|
||||
# :param context: context dictionary that can explicitly mention the company to consider via the 'force_company' key
|
||||
# :raise: osv.except_osv() is any mandatory account or journal is not defined.
|
||||
# """
|
||||
# product_obj=self.pool.get('product.product')
|
||||
# accounts = product_obj.get_product_accounts(cr, uid, move.product_id.id, context)
|
||||
# if move.location_id.valuation_out_account_id:
|
||||
# acc_src = move.location_id.valuation_out_account_id.id
|
||||
# else:
|
||||
# acc_src = accounts['stock_account_input']
|
||||
|
||||
# if move.location_dest_id.valuation_in_account_id:
|
||||
# acc_dest = move.location_dest_id.valuation_in_account_id.id
|
||||
# else:
|
||||
# acc_dest = accounts['stock_account_output']
|
||||
|
||||
# acc_valuation = accounts.get('property_stock_valuation_account_id', False)
|
||||
# journal_id = accounts['stock_journal']
|
||||
|
||||
# if acc_dest == acc_valuation:
|
||||
# raise osv.except_osv(_('Error!'), _('Cannot create Journal Entry, Output Account of this product and Valuation account on category of this product are same.'))
|
||||
|
||||
# if acc_src == acc_valuation:
|
||||
# raise osv.except_osv(_('Error!'), _('Cannot create Journal Entry, Input Account of this product and Valuation account on category of this product are same.'))
|
||||
|
||||
# if not acc_src:
|
||||
# raise osv.except_osv(_('Error!'), _('Please define stock input account for this product or its category: "%s" (id: %d)') % \
|
||||
# (move.product_id.name, move.product_id.id,))
|
||||
# if not acc_dest:
|
||||
# raise osv.except_osv(_('Error!'), _('Please define stock output account for this product or its category: "%s" (id: %d)') % \
|
||||
# (move.product_id.name, move.product_id.id,))
|
||||
# if not journal_id:
|
||||
# raise osv.except_osv(_('Error!'), _('Please define journal on the product category: "%s" (id: %d)') % \
|
||||
# (move.product_id.categ_id.name, move.product_id.categ_id.id,))
|
||||
# if not acc_valuation:
|
||||
# raise osv.except_osv(_('Error!'), _('Please define inventory valuation account on the product category: "%s" (id: %d)') % \
|
||||
# (move.product_id.categ_id.name, move.product_id.categ_id.id,))
|
||||
# return journal_id, acc_src, acc_dest, acc_valuation
|
||||
|
||||
|
||||
##We can use a preliminary type
|
||||
#def get_reference_amount(self, cr, uid, move, qty, context=None):
|
||||
# # if product is set to average price and a specific value was entered in the picking wizard,
|
||||
# # we use it
|
||||
|
||||
# # by default the reference currency is that of the move's company
|
||||
# reference_currency_id = move.company_id.currency_id.id
|
||||
#
|
||||
# #I use
|
||||
# if move.product_id.cost_method != 'standard' and move.price_unit:
|
||||
# reference_amount = move.product_qty * move.price_unit #Using move.price_qty instead of qty to have correct amount
|
||||
# reference_currency_id = move.price_currency_id.id or reference_currency_id
|
||||
|
||||
# # Otherwise we default to the company's valuation price type, considering that the values of the
|
||||
# # valuation field are expressed in the default currency of the move's company.
|
||||
# else:
|
||||
# if context is None:
|
||||
# context = {}
|
||||
# currency_ctx = dict(context, currency_id = move.company_id.currency_id.id)
|
||||
# amount_unit = move.product_id.price_get('standard_price', context=currency_ctx)[move.product_id.id]
|
||||
# reference_amount = amount_unit * qty
|
||||
#
|
||||
# return reference_amount, reference_currency_id
|
||||
|
||||
|
||||
#def _get_reference_accounting_values_for_valuation(self, cr, uid, move, context=None):
|
||||
# """
|
||||
# Return the reference amount and reference currency representing the inventory valuation for this move.
|
||||
# These reference values should possibly be converted before being posted in Journals to adapt to the primary
|
||||
# and secondary currencies of the relevant accounts.
|
||||
# """
|
||||
# product_uom_obj = self.pool.get('product.uom')
|
||||
|
||||
# default_uom = move.product_id.uom_id.id
|
||||
# qty = product_uom_obj._compute_qty(cr, uid, move.product_uom.id, move.product_qty, default_uom)
|
||||
#
|
||||
# reference_amount, reference_currency_id = self.get_reference_amount(cr, uid, move, qty, context=context)
|
||||
# return reference_amount, reference_currency_id
|
||||
|
||||
|
||||
#
|
||||
|
||||
|
||||
#def _create_product_valuation_moves(self, cr, uid, move, matches, context=None):
|
||||
# """
|
||||
# Generate the appropriate accounting moves if the product being moved is subject
|
||||
# to real_time valuation tracking, and the source or the destination location is internal (not both)
|
||||
# This means an in or out move.
|
||||
#
|
||||
# Depending on the matches it will create the necessary moves
|
||||
# """
|
||||
# ctx = context.copy()
|
||||
# ctx['force_company'] = move.company_id.id
|
||||
# valuation = self.pool.get("product.product").browse(cr, uid, move.product_id.id, context=ctx).valuation
|
||||
# move_obj = self.pool.get('account.move')
|
||||
# if valuation == 'real_time':
|
||||
# if context is None:
|
||||
# context = {}
|
||||
# company_ctx = dict(context,force_company=move.company_id.id)
|
||||
# journal_id, acc_src, acc_dest, acc_valuation = self._get_accounting_data_for_valuation(cr, uid, move, context=company_ctx)
|
||||
# reference_amount, reference_currency_id = self._get_reference_accounting_values_for_valuation(cr, uid, move, context=company_ctx)
|
||||
# account_moves = []
|
||||
# # Outgoing moves (or cross-company output part)
|
||||
# if move.location_id.company_id \
|
||||
# and (move.location_id.usage == 'internal' and move.location_dest_id.usage != 'internal'):
|
||||
# #returning goods to supplier
|
||||
# if move.location_dest_id.usage == 'supplier':
|
||||
# account_moves += [(journal_id, self._create_account_move_line(cr, uid, move, matches, acc_valuation, acc_src, reference_amount, reference_currency_id, 'out', context=company_ctx))]
|
||||
# else:
|
||||
# account_moves += [(journal_id, self._create_account_move_line(cr, uid, move, matches, acc_valuation, acc_dest, reference_amount, reference_currency_id, 'out', context=company_ctx))]
|
||||
|
||||
# # Incoming moves (or cross-company input part)
|
||||
# if move.location_dest_id.company_id \
|
||||
# and (move.location_id.usage != 'internal' and move.location_dest_id.usage == 'internal'):
|
||||
# #goods return from customer
|
||||
# if move.location_id.usage == 'customer':
|
||||
# account_moves += [(journal_id, self._create_account_move_line(cr, uid, move, matches, acc_dest, acc_valuation, reference_amount, reference_currency_id, 'in', context=company_ctx))]
|
||||
# else:
|
||||
# account_moves += [(journal_id, self._create_account_move_line(cr, uid, move, matches, acc_src, acc_valuation, reference_amount, reference_currency_id, 'in', context=company_ctx))]
|
||||
# if matches and move.product_id.cost_method in ('fifo', 'lifo'):
|
||||
# outs = {}
|
||||
# match_obj = self.pool.get("stock.move.matching")
|
||||
# for match in match_obj.browse(cr, uid, matches, context=context):
|
||||
# if match.move_out_id.id in outs:
|
||||
# outs[match.move_out_id.id] += [match.id]
|
||||
# else:
|
||||
# outs[match.move_out_id.id] = [match.id]
|
||||
# #When in stock was negative, you will get matches for the in also:
|
||||
# account_moves_neg = []
|
||||
# for out_mov in self.browse(cr, uid, outs.keys(), context=context):
|
||||
# journal_id_out, acc_src_out, acc_dest_out, acc_valuation_out = self._get_accounting_data_for_valuation(cr, uid, out_mov, context=company_ctx)
|
||||
# reference_amount_out, reference_currency_id_out = self._get_reference_accounting_values_for_valuation(cr, uid, out_mov, context=company_ctx)
|
||||
# if out_mov.location_dest_id.usage == 'supplier':
|
||||
# # Is not the way it should be with acc_valuation
|
||||
# account_moves_neg += [(journal_id_out, self._create_account_move_line(cr, uid, out_mov, outs[out_mov.id], acc_valuation_out, acc_src_out, reference_amount_out, reference_currency_id_out, 'out', context=company_ctx))]
|
||||
# else:
|
||||
# account_moves_neg += [(journal_id_out, self._create_account_move_line(cr, uid, out_mov, outs[out_mov.id], acc_valuation_out, acc_dest_out, reference_amount_out, reference_currency_id_out, 'out', context=company_ctx))]
|
||||
# #Create account moves for outs which made stock go negative
|
||||
# for j_id, move_lines in account_moves_neg:
|
||||
# move_obj.create(cr, uid,
|
||||
# {'journal_id': j_id,
|
||||
# 'line_id': move_lines,
|
||||
# 'ref': out_mov.picking_id and out_mov.picking_id.name,
|
||||
# })
|
||||
# for j_id, move_lines in account_moves:
|
||||
# move_obj.create(cr, uid,
|
||||
# {
|
||||
# 'journal_id': j_id,
|
||||
# 'line_id': move_lines,
|
||||
# 'ref': move.picking_id and move.picking_id.name})
|
||||
|
||||
#def _create_account_move_line(self, cr, uid, quant, src_account_id, dest_account_id, context=None):
|
||||
# """
|
||||
# Generate the account.move.line values to post to track the stock valuation difference due to the
|
||||
# processing of the given stock move.
|
||||
# """
|
||||
# move_list = []
|
||||
# # Consists of access rights
|
||||
# # TODO Check if amount_currency is not needed
|
||||
# match_obj = self.pool.get("stock.move.matching")
|
||||
# if type == 'out' and move.product_id.cost_method in ['real']:
|
||||
# for match in match_obj.browse(cr, uid, matches, context=context):
|
||||
# move_list += [(match.qty, match.qty * match.price_unit_out)]
|
||||
# elif type == 'in' and move.product_id.cost_method in ['real']:
|
||||
# move_list = [(move.product_qty, reference_amount)]
|
||||
# else:
|
||||
# move_list = [(move.product_qty, reference_amount)]
|
||||
|
||||
# res = []
|
||||
# for item in move_list:
|
||||
# # prepare default values considering that the destination accounts have the reference_currency_id as their main currency
|
||||
# partner_id = (move.picking_id.partner_id and self.pool.get('res.partner')._find_accounting_partner(move.picking_id.partner_id).id) or False
|
||||
# debit_line_vals = {
|
||||
# 'name': move.name,
|
||||
# 'product_id': move.product_id and move.product_id.id or False,
|
||||
# 'quantity': item[0],
|
||||
# 'product_uom_id': move.product_uom.id,
|
||||
# 'ref': move.picking_id and move.picking_id.name or False,
|
||||
# 'date': time.strftime('%Y-%m-%d'),
|
||||
# 'partner_id': partner_id,
|
||||
# 'debit': item[1],
|
||||
# 'account_id': dest_account_id,
|
||||
# }
|
||||
# credit_line_vals = {
|
||||
# 'name': move.name,
|
||||
# 'product_id': move.product_id and move.product_id.id or False,
|
||||
# 'quantity': item[0],
|
||||
# 'product_uom_id': move.product_uom.id,
|
||||
# 'ref': move.picking_id and move.picking_id.name or False,
|
||||
# 'date': time.strftime('%Y-%m-%d'),
|
||||
# 'partner_id': partner_id,
|
||||
# 'credit': item[1],
|
||||
# 'account_id': src_account_id,
|
||||
# }
|
||||
# res += [(0, 0, debit_line_vals), (0, 0, credit_line_vals)]
|
||||
# return res
|
||||
|
||||
|
||||
#----------------------------------------------------------
|
||||
# Stock Picking
|
||||
|
@ -651,11 +416,6 @@ class stock_picking(osv.osv):
|
|||
'move_lines': fields.one2many('stock.move', 'picking_id', 'Internal Moves', states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}),
|
||||
'auto_picking': fields.boolean('Auto-Picking', states={'done':[('readonly', True)], 'cancel':[('readonly',True)]}),
|
||||
'partner_id': fields.many2one('res.partner', 'Partner', states={'done':[('readonly', True)], 'cancel':[('readonly',True)]}),
|
||||
'invoice_state': fields.selection([
|
||||
("invoiced", "Invoiced"),
|
||||
("2binvoiced", "To Be Invoiced"),
|
||||
("none", "Not Applicable")], "Invoice Control",
|
||||
select=True, required=True, readonly=True, track_visibility='onchange', states={'draft': [('readonly', False)]}),
|
||||
'company_id': fields.many2one('res.company', 'Company', required=True, select=True, states={'done':[('readonly', True)], 'cancel':[('readonly',True)]}),
|
||||
'pack_operation_ids': fields.one2many('stock.pack.operation', 'picking_id', string='Related Packing Operations'),
|
||||
|
||||
|
@ -670,7 +430,6 @@ class stock_picking(osv.osv):
|
|||
'state': 'draft',
|
||||
'move_type': 'direct',
|
||||
'type': 'internal',
|
||||
'invoice_state': 'none',
|
||||
'date': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
|
||||
'company_id': lambda self, cr, uid, c: self.pool.get('res.company')._company_default_get(cr, uid, 'stock.picking', context=c)
|
||||
}
|
||||
|
@ -706,8 +465,6 @@ class stock_picking(osv.osv):
|
|||
default['name'] = '/'
|
||||
default['origin'] = ''
|
||||
default['backorder_id'] = False
|
||||
if 'invoice_state' not in default and picking_obj.invoice_state == 'invoiced':
|
||||
default['invoice_state'] = '2binvoiced'
|
||||
return super(stock_picking, self).copy(cr, uid, id, default, context)
|
||||
|
||||
def fields_view_get(self, cr, uid, view_id=None, view_type=False, context=None, toolbar=False, submenu=False):
|
||||
|
@ -865,262 +622,6 @@ class stock_picking(osv.osv):
|
|||
self.pool.get('stock.move').action_done(cr, uid, todo, context=context)
|
||||
return True
|
||||
|
||||
def get_currency_id(self, cr, uid, picking):
|
||||
return False
|
||||
|
||||
def _get_partner_to_invoice(self, cr, uid, picking, context=None):
|
||||
""" Gets the partner that will be invoiced
|
||||
Note that this function is inherited in the sale and purchase modules
|
||||
@param picking: object of the picking for which we are selecting the partner to invoice
|
||||
@return: object of the partner to invoice
|
||||
"""
|
||||
return picking.partner_id and picking.partner_id.id
|
||||
|
||||
def _get_comment_invoice(self, cr, uid, picking):
|
||||
"""
|
||||
@return: comment string for invoice
|
||||
"""
|
||||
return picking.note or ''
|
||||
|
||||
def _get_price_unit_invoice(self, cr, uid, move_line, type, context=None):
|
||||
""" Gets price unit for invoice
|
||||
@param move_line: Stock move lines
|
||||
@param type: Type of invoice
|
||||
@return: The price unit for the move line
|
||||
"""
|
||||
if context is None:
|
||||
context = {}
|
||||
|
||||
if type in ('in_invoice', 'in_refund'):
|
||||
# Take the user company and pricetype
|
||||
context['currency_id'] = move_line.company_id.currency_id.id
|
||||
amount_unit = move_line.product_id.price_get('standard_price', context=context)[move_line.product_id.id]
|
||||
return amount_unit
|
||||
else:
|
||||
return move_line.product_id.list_price
|
||||
|
||||
def _get_discount_invoice(self, cr, uid, move_line):
|
||||
'''Return the discount for the move line'''
|
||||
return 0.0
|
||||
|
||||
def _get_taxes_invoice(self, cr, uid, move_line, type):
|
||||
""" Gets taxes on invoice
|
||||
@param move_line: Stock move lines
|
||||
@param type: Type of invoice
|
||||
@return: Taxes Ids for the move line
|
||||
"""
|
||||
if type in ('in_invoice', 'in_refund'):
|
||||
taxes = move_line.product_id.supplier_taxes_id
|
||||
else:
|
||||
taxes = move_line.product_id.taxes_id
|
||||
|
||||
if move_line.picking_id and move_line.picking_id.partner_id and move_line.picking_id.partner_id.id:
|
||||
return self.pool.get('account.fiscal.position').map_tax(
|
||||
cr,
|
||||
uid,
|
||||
move_line.picking_id.partner_id.property_account_position,
|
||||
taxes
|
||||
)
|
||||
else:
|
||||
return map(lambda x: x.id, taxes)
|
||||
|
||||
def _get_account_analytic_invoice(self, cr, uid, picking, move_line):
|
||||
return False
|
||||
|
||||
def _invoice_line_hook(self, cr, uid, move_line, invoice_line_id):
|
||||
'''Call after the creation of the invoice line'''
|
||||
return
|
||||
|
||||
def _invoice_hook(self, cr, uid, picking, invoice_id):
|
||||
'''Call after the creation of the invoice'''
|
||||
return
|
||||
|
||||
def _get_invoice_type(self, pick):
|
||||
src_usage = dest_usage = None
|
||||
inv_type = None
|
||||
if pick.invoice_state == '2binvoiced':
|
||||
if pick.move_lines:
|
||||
src_usage = pick.move_lines[0].location_id.usage
|
||||
dest_usage = pick.move_lines[0].location_dest_id.usage
|
||||
if pick.type == 'out' and dest_usage == 'supplier':
|
||||
inv_type = 'in_refund'
|
||||
elif pick.type == 'out' and dest_usage == 'customer':
|
||||
inv_type = 'out_invoice'
|
||||
elif pick.type == 'in' and src_usage == 'supplier':
|
||||
inv_type = 'in_invoice'
|
||||
elif pick.type == 'in' and src_usage == 'customer':
|
||||
inv_type = 'out_refund'
|
||||
else:
|
||||
inv_type = 'out_invoice'
|
||||
return inv_type
|
||||
|
||||
def _prepare_invoice_group(self, cr, uid, picking, partner, invoice, context=None):
|
||||
""" Builds the dict for grouped invoices
|
||||
@param picking: picking object
|
||||
@param partner: object of the partner to invoice (not used here, but may be usefull if this function is inherited)
|
||||
@param invoice: object of the invoice that we are updating
|
||||
@return: dict that will be used to update the invoice
|
||||
"""
|
||||
comment = self._get_comment_invoice(cr, uid, picking)
|
||||
return {
|
||||
'name': (invoice.name or '') + ', ' + (picking.name or ''),
|
||||
'origin': (invoice.origin or '') + ', ' + (picking.name or '') + (picking.origin and (':' + picking.origin) or ''),
|
||||
'comment': (comment and (invoice.comment and invoice.comment + "\n" + comment or comment)) or (invoice.comment and invoice.comment or ''),
|
||||
'date_invoice': context.get('date_inv', False),
|
||||
'user_id': uid,
|
||||
}
|
||||
|
||||
def _prepare_invoice(self, cr, uid, picking, partner, inv_type, journal_id, context=None):
|
||||
""" Builds the dict containing the values for the invoice
|
||||
@param picking: picking object
|
||||
@param partner: object of the partner to invoice
|
||||
@param inv_type: type of the invoice ('out_invoice', 'in_invoice', ...)
|
||||
@param journal_id: ID of the accounting journal
|
||||
@return: dict that will be used to create the invoice object
|
||||
"""
|
||||
if isinstance(partner, int):
|
||||
partner = self.pool.get('res.partner').browse(cr, uid, partner, context=context)
|
||||
if inv_type in ('out_invoice', 'out_refund'):
|
||||
account_id = partner.property_account_receivable.id
|
||||
payment_term = partner.property_payment_term.id or False
|
||||
else:
|
||||
account_id = partner.property_account_payable.id
|
||||
payment_term = partner.property_supplier_payment_term.id or False
|
||||
comment = self._get_comment_invoice(cr, uid, picking)
|
||||
invoice_vals = {
|
||||
'name': picking.name,
|
||||
'origin': (picking.name or '') + (picking.origin and (':' + picking.origin) or ''),
|
||||
'type': inv_type,
|
||||
'account_id': account_id,
|
||||
'partner_id': partner.id,
|
||||
'comment': comment,
|
||||
'payment_term': payment_term,
|
||||
'fiscal_position': partner.property_account_position.id,
|
||||
'date_invoice': context.get('date_inv', False),
|
||||
'company_id': picking.company_id.id,
|
||||
'user_id': uid,
|
||||
}
|
||||
cur_id = self.get_currency_id(cr, uid, picking)
|
||||
if cur_id:
|
||||
invoice_vals['currency_id'] = cur_id
|
||||
if journal_id:
|
||||
invoice_vals['journal_id'] = journal_id
|
||||
return invoice_vals
|
||||
|
||||
def _prepare_invoice_line(self, cr, uid, group, picking, move_line, invoice_id,
|
||||
invoice_vals, context=None):
|
||||
""" Builds the dict containing the values for the invoice line
|
||||
@param group: True or False
|
||||
@param picking: picking object
|
||||
@param: move_line: move_line object
|
||||
@param: invoice_id: ID of the related invoice
|
||||
@param: invoice_vals: dict used to created the invoice
|
||||
@return: dict that will be used to create the invoice line
|
||||
"""
|
||||
if group:
|
||||
name = (picking.name or '') + '-' + move_line.name
|
||||
else:
|
||||
name = move_line.name
|
||||
origin = move_line.picking_id.name or ''
|
||||
if move_line.picking_id.origin:
|
||||
origin += ':' + move_line.picking_id.origin
|
||||
|
||||
if invoice_vals['type'] in ('out_invoice', 'out_refund'):
|
||||
account_id = move_line.product_id.property_account_income.id
|
||||
if not account_id:
|
||||
account_id = move_line.product_id.categ_id.\
|
||||
property_account_income_categ.id
|
||||
else:
|
||||
account_id = move_line.product_id.property_account_expense.id
|
||||
if not account_id:
|
||||
account_id = move_line.product_id.categ_id.\
|
||||
property_account_expense_categ.id
|
||||
if invoice_vals['fiscal_position']:
|
||||
fp_obj = self.pool.get('account.fiscal.position')
|
||||
fiscal_position = fp_obj.browse(cr, uid, invoice_vals['fiscal_position'], context=context)
|
||||
account_id = fp_obj.map_account(cr, uid, fiscal_position, account_id)
|
||||
# set UoS if it's a sale and the picking doesn't have one
|
||||
uos_id = move_line.product_uos and move_line.product_uos.id or False
|
||||
if not uos_id and invoice_vals['type'] in ('out_invoice', 'out_refund'):
|
||||
uos_id = move_line.product_uom.id
|
||||
|
||||
return {
|
||||
'name': name,
|
||||
'origin': origin,
|
||||
'invoice_id': invoice_id,
|
||||
'uos_id': uos_id,
|
||||
'product_id': move_line.product_id.id,
|
||||
'account_id': account_id,
|
||||
'price_unit': self._get_price_unit_invoice(cr, uid, move_line, invoice_vals['type']),
|
||||
'discount': self._get_discount_invoice(cr, uid, move_line),
|
||||
'quantity': move_line.product_uos_qty or move_line.product_qty,
|
||||
'invoice_line_tax_id': [(6, 0, self._get_taxes_invoice(cr, uid, move_line, invoice_vals['type']))],
|
||||
'account_analytic_id': self._get_account_analytic_invoice(cr, uid, picking, move_line),
|
||||
}
|
||||
|
||||
def action_invoice_create(self, cr, uid, ids, journal_id=False,
|
||||
group=False, type='out_invoice', context=None):
|
||||
""" Creates invoice based on the invoice state selected for picking.
|
||||
@param journal_id: Id of journal
|
||||
@param group: Whether to create a group invoice or not
|
||||
@param type: Type invoice to be created
|
||||
@return: Ids of created invoices for the pickings
|
||||
"""
|
||||
if context is None:
|
||||
context = {}
|
||||
|
||||
invoice_obj = self.pool.get('account.invoice')
|
||||
invoice_line_obj = self.pool.get('account.invoice.line')
|
||||
partner_obj = self.pool.get('res.partner')
|
||||
invoices_group = {}
|
||||
res = {}
|
||||
inv_type = type
|
||||
for picking in self.browse(cr, uid, ids, context=context):
|
||||
if picking.invoice_state != '2binvoiced':
|
||||
continue
|
||||
partner = self._get_partner_to_invoice(cr, uid, picking, context=context)
|
||||
if isinstance(partner, int):
|
||||
partner = partner_obj.browse(cr, uid, [partner], context=context)[0]
|
||||
if not partner:
|
||||
raise osv.except_osv(_('Error, no partner!'),
|
||||
_('Please put a partner on the picking list if you want to generate invoice.'))
|
||||
|
||||
if not inv_type:
|
||||
inv_type = self._get_invoice_type(picking)
|
||||
|
||||
if group and partner.id in invoices_group:
|
||||
invoice_id = invoices_group[partner.id]
|
||||
invoice = invoice_obj.browse(cr, uid, invoice_id)
|
||||
invoice_vals_group = self._prepare_invoice_group(cr, uid, picking, partner, invoice, context=context)
|
||||
invoice_obj.write(cr, uid, [invoice_id], invoice_vals_group, context=context)
|
||||
else:
|
||||
invoice_vals = self._prepare_invoice(cr, uid, picking, partner, inv_type, journal_id, context=context)
|
||||
invoice_id = invoice_obj.create(cr, uid, invoice_vals, context=context)
|
||||
invoices_group[partner.id] = invoice_id
|
||||
res[picking.id] = invoice_id
|
||||
for move_line in picking.move_lines:
|
||||
if move_line.state == 'cancel':
|
||||
continue
|
||||
if move_line.scrapped:
|
||||
# do no invoice scrapped products
|
||||
continue
|
||||
vals = self._prepare_invoice_line(cr, uid, group, picking, move_line,
|
||||
invoice_id, invoice_vals, context=context)
|
||||
if vals:
|
||||
invoice_line_id = invoice_line_obj.create(cr, uid, vals, context=context)
|
||||
self._invoice_line_hook(cr, uid, move_line, invoice_line_id)
|
||||
|
||||
invoice_obj.button_compute(cr, uid, [invoice_id], context=context,
|
||||
set_total=(inv_type in ('in_invoice', 'in_refund')))
|
||||
self.write(cr, uid, [picking.id], {
|
||||
'invoice_state': 'invoiced',
|
||||
}, context=context)
|
||||
self._invoice_hook(cr, uid, picking, invoice_id)
|
||||
self.write(cr, uid, res.keys(), {
|
||||
'invoice_state': 'invoiced',
|
||||
}, context=context)
|
||||
return res
|
||||
|
||||
def test_done(self, cr, uid, ids, context=None):
|
||||
""" Test whether the move lines are done or not.
|
||||
@return: True or False
|
||||
|
@ -1165,9 +666,6 @@ class stock_picking(osv.osv):
|
|||
|
||||
return super(stock_picking, self).unlink(cr, uid, ids, context=context)
|
||||
|
||||
|
||||
|
||||
|
||||
# FP Note: review all methods aboce this line for stock.picking
|
||||
|
||||
#TODO move this in another class?
|
||||
|
@ -2558,24 +2056,26 @@ class stock_inventory(osv.osv):
|
|||
return True
|
||||
|
||||
def action_cancel_inventory(self, cr, uid, ids, context=None):
|
||||
""" Cancels both stock move and inventory
|
||||
@return: True
|
||||
"""
|
||||
move_obj = self.pool.get('stock.move')
|
||||
account_move_obj = self.pool.get('account.move')
|
||||
for inv in self.browse(cr, uid, ids, context=context):
|
||||
move_obj.action_cancel(cr, uid, [x.id for x in inv.move_ids], context=context)
|
||||
for move in inv.move_ids:
|
||||
account_move_ids = account_move_obj.search(cr, uid, [('name', '=', move.name)])
|
||||
if account_move_ids:
|
||||
account_move_data_l = account_move_obj.read(cr, uid, account_move_ids, ['state'], context=context)
|
||||
for account_move in account_move_data_l:
|
||||
if account_move['state'] == 'posted':
|
||||
raise osv.except_osv(_('User Error!'),
|
||||
_('In order to cancel this inventory, you must first unpost related journal entries.'))
|
||||
account_move_obj.unlink(cr, uid, [account_move['id']], context=context)
|
||||
self.write(cr, uid, [inv.id], {'state': 'cancel'}, context=context)
|
||||
return True
|
||||
#TODO test
|
||||
self.action_cancel_draft(cr, uid, ids, context=context)
|
||||
#""" Cancels both stock move and inventory
|
||||
#@return: True
|
||||
#"""
|
||||
#move_obj = self.pool.get('stock.move')
|
||||
#account_move_obj = self.pool.get('account.move')
|
||||
#for inv in self.browse(cr, uid, ids, context=context):
|
||||
# move_obj.action_cancel(cr, uid, [x.id for x in inv.move_ids], context=context)
|
||||
# for move in inv.move_ids:
|
||||
# account_move_ids = account_move_obj.search(cr, uid, [('name', '=', move.name)])
|
||||
# if account_move_ids:
|
||||
# account_move_data_l = account_move_obj.read(cr, uid, account_move_ids, ['state'], context=context)
|
||||
# for account_move in account_move_data_l:
|
||||
# if account_move['state'] == 'posted':
|
||||
# raise osv.except_osv(_('User Error!'),
|
||||
# _('In order to cancel this inventory, you must first unpost related journal entries.'))
|
||||
# account_move_obj.unlink(cr, uid, [account_move['id']], context=context)
|
||||
# self.write(cr, uid, [inv.id], {'state': 'cancel'}, context=context)
|
||||
#return True
|
||||
|
||||
|
||||
class stock_inventory_line(osv.osv):
|
||||
|
|
|
@ -12,18 +12,6 @@
|
|||
watch your stock valuation, and track production lots upstream and downstream (based on serial numbers).</p>]]></field>
|
||||
</record>
|
||||
|
||||
<record id="stock_journal_sequence" model="ir.sequence">
|
||||
<field name="name">Stock Journal Sequence</field>
|
||||
<field eval="3" name="padding"/>
|
||||
<field name="prefix">STJ/%(year)s/</field>
|
||||
</record>
|
||||
<record forcecreate="1" id="stock_journal" model="account.journal">
|
||||
<field name="name">Stock Journal</field>
|
||||
<field name="code">STJ</field>
|
||||
<field name="type">general</field>
|
||||
<field name="sequence_id" ref="stock_journal_sequence"/>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
</record>
|
||||
|
||||
<!--
|
||||
Request link
|
||||
|
|
|
@ -271,13 +271,6 @@
|
|||
<field name="lot_input_id" ref="stock_location_shop1"/>
|
||||
</record>
|
||||
|
||||
<record forcecreate="True" id="property_stock_valuation_account_id" model="ir.property">
|
||||
<field name="name">property_stock_valuation_account_id</field>
|
||||
<field name="fields_id" search="[('model','=','product.category'),('name','=','property_stock_valuation_account_id')]"/>
|
||||
<field eval="'account.account,'+str(ref('account.stk'))" model="account.account" name="value"/>
|
||||
<field name="company_id" ref="base.main_company"/>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
|
||||
|
|
|
@ -38,8 +38,6 @@
|
|||
property_stock_inventory: location_opening
|
||||
valuation: real_time
|
||||
cost_method: average
|
||||
property_stock_account_input: account.o_expense
|
||||
property_stock_account_output: account.o_income
|
||||
description: Ice cream can be mass-produced and thus is widely available in developed parts of the world. Ice cream can be purchased in large cartons (vats and squrounds) from supermarkets and grocery stores, in smaller quantities from ice cream shops, convenience stores, and milk bars, and in individual servings from small carts or vans at public events.
|
||||
|
||||
-
|
||||
|
|
|
@ -59,7 +59,7 @@
|
|||
<field name="product_uom" groups="product.group_uom"/>
|
||||
<field domain="[('usage','=','internal')]" name="location_id"/>
|
||||
<button name="%(stock.action_view_stock_inventory_line_split)d"
|
||||
string="Split Inventory Line" groups="stock.group_inventory_valuation"
|
||||
string="Split Inventory Line"
|
||||
type="action" icon="gtk-justify-fill"/>
|
||||
</group>
|
||||
</form>
|
||||
|
@ -380,13 +380,10 @@
|
|||
<field name="scrap_location"/>
|
||||
<field name="active"/>
|
||||
</group>
|
||||
<group string="Localization">
|
||||
<group string="Localization" name="localization">
|
||||
<field name="posx"/>
|
||||
<field name="posy"/>
|
||||
<field name="posz"/>
|
||||
</group><group string="Accounting Information" attrs="{'invisible':[('usage','not in',('inventory','production'))]}">
|
||||
<field name="valuation_in_account_id"/>
|
||||
<field name="valuation_out_account_id"/>
|
||||
</group>
|
||||
</group>
|
||||
<separator string="Additional Information"/>
|
||||
|
@ -573,7 +570,6 @@
|
|||
<field name="origin"/>
|
||||
<field name="date"/>
|
||||
<field name="min_date"/>
|
||||
<field name="invoice_state"/>
|
||||
<field name="stock_journal_id" widget="selection"/>
|
||||
<field name="state"/>
|
||||
</tree>
|
||||
|
@ -592,7 +588,6 @@
|
|||
<!-- <button name="check_assign" states="confirmed" string="Check Availability" type="object"/> -->
|
||||
<button name="force_assign" states="confirmed" string="Force Availability" type="object" class="oe_highlight" groups="base.group_user"/>
|
||||
<button name="action_process" states="assigned" string="Confirm & Transfer" groups="stock.group_stock_user" type="object" class="oe_highlight"/>
|
||||
<button name="%(action_stock_invoice_onshipping)d" string="Create Invoice/Refund" attrs="{'invisible': ['|','|',('state','<>','done'),('invoice_state','=','invoiced'),('invoice_state','=','none')]}" type="action" class="oe_highlight" groups="base.group_user"/>
|
||||
<button name="%(act_stock_return_picking)d" string="Reverse Transfer" states="done" type="action" groups="base.group_user"/>
|
||||
<button name="button_cancel" states="assigned,confirmed,draft" string="Cancel Transfer" groups="base.group_user"/>
|
||||
<field name="state" widget="statusbar" statusbar_visible="draft,assigned,done" statusbar_colors='{"shipping_except":"red","invoice_except":"red","waiting_date":"blue"}'/>
|
||||
|
@ -605,8 +600,7 @@
|
|||
<group>
|
||||
<field name="partner_id"/>
|
||||
<field name="backorder_id" readonly="1" attrs="{'invisible': [('backorder_id','=',False)]}"/>
|
||||
<field name="invoice_state" string="Invoice Control" groups="account.group_account_invoice" attrs="{'invisible':[('invoice_state', '=', 'none')]}"/>
|
||||
<field name="stock_journal_id" widget="selection" groups="account.group_account_user"/>
|
||||
<field name="stock_journal_id" widget="selection"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="date"/>
|
||||
|
@ -719,8 +713,7 @@
|
|||
<field name="min_date"/>
|
||||
<field name="date"/>
|
||||
<field name="backorder_id"/>
|
||||
<field name="stock_journal_id" groups="account.group_account_user"/>
|
||||
<field name="invoice_state"/>
|
||||
<field name="stock_journal_id"/>
|
||||
<field name="state"/>
|
||||
</tree>
|
||||
</field>
|
||||
|
@ -770,7 +763,6 @@
|
|||
<separator/>
|
||||
<filter icon="terp-accessories-archiver-minus" string="Back Orders" domain="[('backorder_id', '!=', False)]" help="Is a Back Order"/>
|
||||
<separator/>
|
||||
<filter icon="terp-dolar" name="to_invoice" string="To Invoice" domain="[('invoice_state','=','2binvoiced')]" help="Delivery orders to invoice"/>
|
||||
<field name="partner_id" filter_domain="[('partner_id','child_of',self)]"/>
|
||||
<field name="stock_journal_id"/>
|
||||
<field name="company_id" groups="base.group_multi_company"/>
|
||||
|
@ -848,8 +840,7 @@
|
|||
<field name="origin"/>
|
||||
<field name="date"/>
|
||||
<field name="min_date"/>
|
||||
<field name="invoice_state" groups="account.group_account_invoice"/>
|
||||
<field name="stock_journal_id" widget="selection" groups="account.group_account_user"/>
|
||||
<field name="stock_journal_id" widget="selection"/>
|
||||
<field name="state"/>
|
||||
</tree>
|
||||
</field>
|
||||
|
@ -893,7 +884,6 @@
|
|||
<separator/>
|
||||
<filter icon="terp-accessories-archiver-minus" string="Back Orders" domain="[('backorder_id', '!=', False)]" help="Is a Back Order"/>
|
||||
<separator/>
|
||||
<filter string="To Invoice" name="to_invoice" icon="terp-dolar" domain="[('invoice_state', '=', '2binvoiced')]"/>
|
||||
<field name="partner_id" filter_domain="[('partner_id','child_of',self)]"/>
|
||||
<field name="stock_journal_id"/>
|
||||
<field name="product_id"/>
|
||||
|
|
|
@ -153,23 +153,6 @@
|
|||
assert move.lot_id.name in ['incoming_lot0', 'incoming_lot1', 'incoming_lot2', 'incoming_lot3'], "lot does not correspond."
|
||||
assert move.product_qty == 10, "qty does not correspond per production lot."
|
||||
context.update({'active_model':'stock.move', 'active_id':move_ids[0],'active_ids': move_ids})
|
||||
-
|
||||
I check the stock valuation account entries.
|
||||
-
|
||||
!python {model: account.move}: |
|
||||
incomming_shipment = self.pool.get('stock.picking').browse(cr, uid, ref('incomming_shipment'), context=context)
|
||||
account_move_ids = self.search(cr, uid, [('ref','=',incomming_shipment.name)])
|
||||
assert len(account_move_ids), "account move should be created."
|
||||
account_move = self.browse(cr, uid, account_move_ids[0], context=context)
|
||||
assert len(account_move.line_id) == len(incomming_shipment.move_lines) + 1, 'accuont entries are not correspond.'
|
||||
for account_move_line in account_move.line_id:
|
||||
for stock_move in incomming_shipment.move_lines:
|
||||
if account_move_line.account_id.id == stock_move.product_id.property_stock_account_input.id:
|
||||
assert account_move_line.credit == 800.0, "Credit amount does not correspond."
|
||||
assert account_move_line.debit == 0.0, "Debit amount does not correspond."
|
||||
else:
|
||||
assert account_move_line.credit == 0.0, "Credit amount does not correspond."
|
||||
assert account_move_line.debit == 800.0, "Debit amount does not correspond."
|
||||
-
|
||||
I consume 1 kgm ice-cream from each incoming lots into internal production.
|
||||
-
|
||||
|
|
|
@ -27,9 +27,7 @@ import stock_partial_move
|
|||
import stock_inventory_merge
|
||||
import stock_fill_inventory
|
||||
import stock_inventory_line_split
|
||||
import stock_invoice_onshipping
|
||||
import stock_location_product
|
||||
import stock_change_standard_price
|
||||
import stock_return_picking
|
||||
import stock_change_product_qty
|
||||
import make_procurement_product
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
# -*- 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/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
import product
|
||||
import stock_account
|
||||
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
|
@ -0,0 +1,94 @@
|
|||
# -*- 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/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
{
|
||||
'name': 'WMS Accounting',
|
||||
'version': '1.1',
|
||||
'author': 'OpenERP SA',
|
||||
'summary': 'Inventory, Logistic, Valuation, Accounting',
|
||||
'description' : """
|
||||
TODO
|
||||
======================
|
||||
TODO
|
||||
|
||||
Key Features
|
||||
------------
|
||||
* Stock Valuation (periodical or automatic)
|
||||
* Invoice from Picking
|
||||
|
||||
Dashboard / Reports for Warehouse Management will include:
|
||||
----------------------------------------------------------
|
||||
* TODO
|
||||
""",
|
||||
'website': 'http://www.openerp.com',
|
||||
'images': [],
|
||||
'depends': ['stock', 'account'],
|
||||
'category': 'Hidden',
|
||||
'sequence': 16,
|
||||
'demo': [
|
||||
# 'stock_demo.xml',
|
||||
# 'procurement_demo.xml',
|
||||
# 'stock_orderpoint.xml',
|
||||
# 'stock_demo.yml',
|
||||
],
|
||||
'data': [
|
||||
# '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',
|
||||
# 'wizard/stock_invoice_onshipping_view.xml',
|
||||
# 'wizard/stock_inventory_merge_view.xml',
|
||||
# 'wizard/stock_location_product_view.xml',
|
||||
# 'wizard/stock_splitinto_view.xml',
|
||||
# 'wizard/stock_inventory_line_split_view.xml',
|
||||
# 'wizard/stock_change_standard_price_view.xml',
|
||||
# 'wizard/stock_return_picking_view.xml',
|
||||
# 'wizard/make_procurement_view.xml',
|
||||
# 'wizard/mrp_procurement_view.xml',
|
||||
# 'wizard/orderpoint_procurement_view.xml',
|
||||
# 'stock_workflow.xml',
|
||||
# 'stock_incoterms.xml',
|
||||
# 'stock_report.xml',
|
||||
# 'stock_view.xml',
|
||||
# 'stock_sequence.xml',
|
||||
# 'product_data.xml',
|
||||
'product_view.xml',
|
||||
# 'partner_view.xml',
|
||||
# 'report/report_stock_move_view.xml',
|
||||
# 'report/report_stock_view.xml',
|
||||
# 'board_warehouse_view.xml',
|
||||
# 'res_config_view.xml',
|
||||
],
|
||||
'test': [
|
||||
# 'test/inventory.yml',
|
||||
# 'test/move.yml',
|
||||
# 'test/shipment.yml',
|
||||
],
|
||||
'installable': True,
|
||||
'application': True,
|
||||
'auto_install': True,
|
||||
}
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
|
@ -0,0 +1,231 @@
|
|||
# -*- 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 openerp.osv import fields, osv
|
||||
from openerp.tools.translate import _
|
||||
|
||||
class product_product(osv.osv):
|
||||
_inherit = "product.product"
|
||||
|
||||
def get_product_accounts(self, cr, uid, product_id, context=None):
|
||||
""" To get the stock input account, stock output account and stock journal related to product.
|
||||
@param product_id: product id
|
||||
@return: dictionary which contains information regarding stock input account, stock output account and stock journal
|
||||
"""
|
||||
if context is None:
|
||||
context = {}
|
||||
product_obj = self.pool.get('product.product').browse(cr, uid, product_id, context=context)
|
||||
|
||||
stock_input_acc = product_obj.property_stock_account_input and product_obj.property_stock_account_input.id or False
|
||||
if not stock_input_acc:
|
||||
stock_input_acc = product_obj.categ_id.property_stock_account_input_categ and product_obj.categ_id.property_stock_account_input_categ.id or False
|
||||
|
||||
stock_output_acc = product_obj.property_stock_account_output and product_obj.property_stock_account_output.id or False
|
||||
if not stock_output_acc:
|
||||
stock_output_acc = product_obj.categ_id.property_stock_account_output_categ and product_obj.categ_id.property_stock_account_output_categ.id or False
|
||||
|
||||
journal_id = product_obj.categ_id.property_stock_journal and product_obj.categ_id.property_stock_journal.id or False
|
||||
account_valuation = product_obj.categ_id.property_stock_valuation_account_id and product_obj.categ_id.property_stock_valuation_account_id.id or False
|
||||
return {
|
||||
'stock_account_input': stock_input_acc,
|
||||
'stock_account_output': stock_output_acc,
|
||||
'stock_journal': journal_id,
|
||||
'property_stock_valuation_account_id': account_valuation
|
||||
}
|
||||
|
||||
# FP Note:;too complex, not good, should be implemented at quant level TODO
|
||||
def do_change_standard_price(self, cr, uid, ids, datas, context=None):
|
||||
""" Changes the Standard Price of Product and creates an account move accordingly.
|
||||
@param datas : dict. contain default datas like new_price, stock_output_account, stock_input_account, stock_journal
|
||||
@param context: A standard dictionary
|
||||
@return:
|
||||
|
||||
"""
|
||||
location_obj = self.pool.get('stock.location')
|
||||
move_obj = self.pool.get('account.move')
|
||||
move_line_obj = self.pool.get('account.move.line')
|
||||
if context is None:
|
||||
context = {}
|
||||
|
||||
new_price = datas.get('new_price', 0.0)
|
||||
stock_output_acc = datas.get('stock_output_account', False)
|
||||
stock_input_acc = datas.get('stock_input_account', False)
|
||||
journal_id = datas.get('stock_journal', False)
|
||||
product_obj=self.browse(cr, uid, ids, context=context)[0]
|
||||
account_valuation = product_obj.categ_id.property_stock_valuation_account_id
|
||||
account_valuation_id = account_valuation and account_valuation.id or False
|
||||
if not account_valuation_id: raise osv.except_osv(_('Error!'), _('Specify valuation Account for Product Category: %s.') % (product_obj.categ_id.name))
|
||||
move_ids = []
|
||||
loc_ids = location_obj.search(cr, uid,[('usage','=','internal')])
|
||||
for rec_id in ids:
|
||||
for location in location_obj.browse(cr, uid, loc_ids, context=context):
|
||||
c = context.copy()
|
||||
c.update({
|
||||
'location': location.id,
|
||||
'compute_child': False
|
||||
})
|
||||
|
||||
product = self.browse(cr, uid, rec_id, context=c)
|
||||
qty = product.qty_available
|
||||
diff = product.standard_price - new_price
|
||||
if not diff: raise osv.except_osv(_('Error!'), _("No difference between standard price and new price!"))
|
||||
if qty:
|
||||
company_id = location.company_id and location.company_id.id or False
|
||||
if not company_id: raise osv.except_osv(_('Error!'), _('Please specify company in Location.'))
|
||||
#
|
||||
# Accounting Entries
|
||||
#
|
||||
if not journal_id:
|
||||
journal_id = product.categ_id.property_stock_journal and product.categ_id.property_stock_journal.id or False
|
||||
if not journal_id:
|
||||
raise osv.except_osv(_('Error!'),
|
||||
_('Please define journal '\
|
||||
'on the product category: "%s" (id: %d).') % \
|
||||
(product.categ_id.name,
|
||||
product.categ_id.id,))
|
||||
move_id = move_obj.create(cr, uid, {
|
||||
'journal_id': journal_id,
|
||||
'company_id': company_id
|
||||
})
|
||||
|
||||
move_ids.append(move_id)
|
||||
|
||||
|
||||
if diff > 0:
|
||||
if not stock_input_acc:
|
||||
stock_input_acc = product.\
|
||||
property_stock_account_input.id
|
||||
if not stock_input_acc:
|
||||
stock_input_acc = product.categ_id.\
|
||||
property_stock_account_input_categ.id
|
||||
if not stock_input_acc:
|
||||
raise osv.except_osv(_('Error!'),
|
||||
_('Please define stock input account ' \
|
||||
'for this product: "%s" (id: %d).') % \
|
||||
(product.name,
|
||||
product.id,))
|
||||
amount_diff = qty * diff
|
||||
move_line_obj.create(cr, uid, {
|
||||
'name': product.name,
|
||||
'account_id': stock_input_acc,
|
||||
'debit': amount_diff,
|
||||
'move_id': move_id,
|
||||
})
|
||||
move_line_obj.create(cr, uid, {
|
||||
'name': product.categ_id.name,
|
||||
'account_id': account_valuation_id,
|
||||
'credit': amount_diff,
|
||||
'move_id': move_id
|
||||
})
|
||||
elif diff < 0:
|
||||
if not stock_output_acc:
|
||||
stock_output_acc = product.\
|
||||
property_stock_account_output.id
|
||||
if not stock_output_acc:
|
||||
stock_output_acc = product.categ_id.\
|
||||
property_stock_account_output_categ.id
|
||||
if not stock_output_acc:
|
||||
raise osv.except_osv(_('Error!'),
|
||||
_('Please define stock output account ' \
|
||||
'for this product: "%s" (id: %d).') % \
|
||||
(product.name,
|
||||
product.id,))
|
||||
amount_diff = qty * -diff
|
||||
move_line_obj.create(cr, uid, {
|
||||
'name': product.name,
|
||||
'account_id': stock_output_acc,
|
||||
'credit': amount_diff,
|
||||
'move_id': move_id
|
||||
})
|
||||
move_line_obj.create(cr, uid, {
|
||||
'name': product.categ_id.name,
|
||||
'account_id': account_valuation_id,
|
||||
'debit': amount_diff,
|
||||
'move_id': move_id
|
||||
})
|
||||
|
||||
self.write(cr, uid, rec_id, {'standard_price': new_price})
|
||||
|
||||
return move_ids
|
||||
|
||||
_columns = {
|
||||
'valuation':fields.property(type='selection', selection=[('manual_periodic', 'Periodical (manual)'),
|
||||
('real_time','Real Time (automated)'),], string = 'Inventory Valuation',
|
||||
help="If real-time valuation is enabled for a product, the system will automatically write journal entries corresponding to stock moves." \
|
||||
"The inventory variation account set on the product category will represent the current inventory value, and the stock input and stock output account will hold the counterpart moves for incoming and outgoing products."
|
||||
, required=True),
|
||||
}
|
||||
|
||||
_defaults = {
|
||||
'valuation': 'manual_periodic',
|
||||
}
|
||||
|
||||
|
||||
class product_template(osv.osv):
|
||||
_name = 'product.template'
|
||||
_inherit = 'product.template'
|
||||
_columns = {
|
||||
'property_stock_account_input': fields.property(
|
||||
type='many2one',
|
||||
relation='account.account',
|
||||
string='Stock Input Account',
|
||||
help="When doing real-time inventory valuation, counterpart journal items for all incoming stock moves will be posted in this account, unless "
|
||||
"there is a specific valuation account set on the source location. When not set on the product, the one from the product category is used."),
|
||||
'property_stock_account_output': fields.property(
|
||||
type='many2one',
|
||||
relation='account.account',
|
||||
string='Stock Output Account',
|
||||
help="When doing real-time inventory valuation, counterpart journal items for all outgoing stock moves will be posted in this account, unless "
|
||||
"there is a specific valuation account set on the destination location. When not set on the product, the one from the product category is used."),
|
||||
}
|
||||
|
||||
|
||||
class product_category(osv.osv):
|
||||
_inherit = 'product.category'
|
||||
_columns = {
|
||||
'property_stock_journal': fields.property(
|
||||
relation='account.journal',
|
||||
type='many2one',
|
||||
string='Stock Journal',
|
||||
help="When doing real-time inventory valuation, this is the Accounting Journal in which entries will be automatically posted when stock moves are processed."),
|
||||
'property_stock_account_input_categ': fields.property(
|
||||
type='many2one',
|
||||
relation='account.account',
|
||||
string='Stock Input Account',
|
||||
help="When doing real-time inventory valuation, counterpart journal items for all incoming stock moves will be posted in this account, unless "
|
||||
"there is a specific valuation account set on the source location. This is the default value for all products in this category. It "
|
||||
"can also directly be set on each product"),
|
||||
'property_stock_account_output_categ': fields.property(
|
||||
type='many2one',
|
||||
relation='account.account',
|
||||
string='Stock Output Account',
|
||||
help="When doing real-time inventory valuation, counterpart journal items for all outgoing stock moves will be posted in this account, unless "
|
||||
"there is a specific valuation account set on the destination location. This is the default value for all products in this category. It "
|
||||
"can also directly be set on each product"),
|
||||
'property_stock_valuation_account_id': fields.property(
|
||||
type='many2one',
|
||||
relation='account.account',
|
||||
string="Stock Valuation Account",
|
||||
help="When real-time inventory valuation is enabled on a product, this account will hold the current value of the products.",),
|
||||
}
|
||||
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
|
@ -0,0 +1,91 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
|
||||
<record id="view_category_property_form" model="ir.ui.view">
|
||||
<field name="name">product.category.stock.property.form.inherit</field>
|
||||
<field name="model">product.category</field>
|
||||
<field name="inherit_id" ref="account.view_category_property_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<data>
|
||||
<group name="account_property" position="after">
|
||||
<group name="account_stock_property" string="Account Stock Properties" colspan="2">
|
||||
<field name="property_stock_account_input_categ" domain="[('type','<>','view'),('type','<>','consolidation')]"/>
|
||||
<field name="property_stock_account_output_categ" domain="[('type','<>','view'),('type','<>','consolidation')]"/>
|
||||
<field name="property_stock_valuation_account_id" domain="[('type','<>','view'), ('type','<>','consolidation')]"/>
|
||||
<field name="property_stock_journal"/>
|
||||
</group>
|
||||
</group>
|
||||
</data>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_template_property_form" model="ir.ui.view">
|
||||
<field name="name">product.template.stock.property.form.inherit</field>
|
||||
<field name="model">product.template</field>
|
||||
<field name="inherit_id" ref="product.product_template_form_view"/>
|
||||
<field name="arch" type="xml">
|
||||
<group name="delay" position="inside">
|
||||
<field name="sale_delay" attrs="{'readonly':[('sale_ok','=',False)]}"/>
|
||||
</group>
|
||||
<group name="delay" position="after">
|
||||
<group name="store" string="Storage Location">
|
||||
<field name="loc_rack"/>
|
||||
<field name="loc_row"/>
|
||||
<field name="loc_case"/>
|
||||
</group>
|
||||
</group>
|
||||
<page position="after" string="Information">
|
||||
<page name="properties" position="inside">
|
||||
<group string="Accounting Entries">
|
||||
<field name="property_stock_account_input" domain="[('type','<>','view'),('type','<>','consolidation')]"/>
|
||||
<field name="property_stock_account_output" domain="[('type','<>','view'),('type','<>','consolidation')]"/>
|
||||
</group>
|
||||
</page>
|
||||
</page>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_product_standard_price_form" model="ir.ui.view">
|
||||
<field name="name">product.product.standard.price.form.inherit</field>
|
||||
<field name="model">product.product</field>
|
||||
<field name="inherit_id" ref="product.product_normal_form_view"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="standard_price" position="replace" version="7.0">
|
||||
<label string="Cost Price" for="standard_price" align="1.0" groups="base.group_user"/>
|
||||
<div groups="base.group_user">
|
||||
<field name="standard_price" attrs="{'readonly':[('valuation','=','real_time'), ('cost_method', 'in', ['standard', 'average'])]}" nolabel="1"/>
|
||||
<button name="%(action_view_change_standard_price)d" string="- update"
|
||||
type="action" attrs="{'invisible':['|', ('valuation','!=', 'real_time'), ('cost_method', 'not in', ['standard', 'average'])]}"
|
||||
class="oe_link" groups="product.group_costing_method"/>
|
||||
</div>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_normal_property_acc_form" model="ir.ui.view">
|
||||
<field name="name">product.normal.stock.acc.property.form.inherit</field>
|
||||
<field name="model">product.product</field>
|
||||
<field name="inherit_id" ref="product.product_normal_form_view"/>
|
||||
<field name="priority">26</field>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//group[@name='properties']" position="before">
|
||||
<group groups="stock.group_inventory_valuation">
|
||||
<separator string="Inventory Valuation" colspan="4"/>
|
||||
<group colspan="2" col="2">
|
||||
<field name="valuation" attrs="{'readonly':[('type', '=', 'service')]}"/>
|
||||
</group>
|
||||
<group colspan="2" col="2">
|
||||
<field name="property_stock_account_input" attrs="{'invisible':[('valuation', '!=', 'real_time')]}"
|
||||
domain="[('type','<>','view'),('type','<>','consolidation')]"/>
|
||||
<field name="property_stock_account_output" attrs="{'invisible':[('valuation', '!=', 'real_time')]}"
|
||||
domain="[('type','<>','view'),('type','<>','consolidation')]"/>
|
||||
</group>
|
||||
</group>
|
||||
<newline/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
|
@ -0,0 +1,7 @@
|
|||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_account_invoice_user,account.invoice stock user,account.model_account_invoice,stock.group_stock_user,1,1,1,0
|
||||
access_account_invoice_line_user,account.invoice.line stock user,account.model_account_invoice_line,stock.group_stock_user,1,1,1,0
|
||||
access_account_invoice_tax_user,account.invoice.tax stock user,account.model_account_invoice_tax,stock.group_stock_user,1,1,1,0
|
||||
access_account_journal_user,account.journal stock user,account.model_account_journal,stock.group_stock_user,1,0,0,0
|
||||
access_account_fiscalyear,account.fiscalyear stock user,account.model_account_fiscalyear,stock.group_stock_user,1,0,0,0
|
||||
access_account_account_stock_manager,account.account stock manager,account.model_account_account,stock.group_stock_manager,1,0,0,0
|
|
|
@ -0,0 +1,634 @@
|
|||
# -*- 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 datetime import datetime
|
||||
from dateutil.relativedelta import relativedelta
|
||||
import time
|
||||
from operator import itemgetter
|
||||
from itertools import groupby
|
||||
|
||||
from openerp.osv import fields, osv
|
||||
from openerp.tools.translate import _
|
||||
from openerp import netsvc
|
||||
from openerp import tools
|
||||
from openerp.tools import float_compare, DEFAULT_SERVER_DATETIME_FORMAT
|
||||
import openerp.addons.decimal_precision as dp
|
||||
import logging
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
#----------------------------------------------------------
|
||||
# Stock Location
|
||||
#----------------------------------------------------------
|
||||
|
||||
class stock_location(osv.osv):
|
||||
_inherit = "stock.location"
|
||||
|
||||
_columns = {
|
||||
'valuation_in_account_id': fields.many2one('account.account', 'Stock Valuation Account (Incoming)', domain = [('type','=','other')],
|
||||
help="Used for real-time inventory valuation. When set on a virtual location (non internal type), "
|
||||
"this account will be used to hold the value of products being moved from an internal location "
|
||||
"into this location, instead of the generic Stock Output Account set on the product. "
|
||||
"This has no effect for internal locations."),
|
||||
'valuation_out_account_id': fields.many2one('account.account', 'Stock Valuation Account (Outgoing)', domain = [('type','=','other')],
|
||||
help="Used for real-time inventory valuation. When set on a virtual location (non internal type), "
|
||||
"this account will be used to hold the value of products being moved out of this location "
|
||||
"and into an internal location, instead of the generic Stock Output Account set on the product. "
|
||||
"This has no effect for internal locations."),
|
||||
}
|
||||
|
||||
#----------------------------------------------------------
|
||||
# Quants
|
||||
#----------------------------------------------------------
|
||||
|
||||
class stock_quant(osv.osv):
|
||||
_inherit = "stock.quant"
|
||||
|
||||
# FP Note: this is where we should post accounting entries for adjustment
|
||||
def _price_update(self, cr, uid, quant, newprice, context=None):
|
||||
super(stock_quant, self)._price_update(cr, uid, quant, newprice, context=context)
|
||||
# TODO: generate accounting entries
|
||||
|
||||
"""
|
||||
Accounting Valuation Entries
|
||||
|
||||
location_from: can be None if it's a new quant
|
||||
"""
|
||||
def _account_entry_move(self, cr, uid, quant, location_from, location_to, move, context=None):
|
||||
if quant.product_id.valuation <> 'realtime':
|
||||
return False
|
||||
company_from = self._location_owner(cr, uid, quant, location_from, context=context)
|
||||
company_to = self._location_owner(cr, uid, quant, location_to, context=context)
|
||||
if company_from == company_to:
|
||||
return False
|
||||
|
||||
# Create Journal Entry for products arriving in the company
|
||||
if company_to:
|
||||
pass
|
||||
|
||||
# Create Journal Entry for products leaving the company
|
||||
if company_from:
|
||||
pass
|
||||
|
||||
def move_single_quant(self, cr, uid, quant, qty, move, context=None):
|
||||
super(stock_quant, self).move_single_quant(cr, uid, quant, qty, move, context=context)
|
||||
self._account_entry_move(cr, uid, quant, location_from, location_to, move, context=context)
|
||||
|
||||
|
||||
# TODO: move this code on the _account_entry_move method above. Should be simpler
|
||||
#
|
||||
#def _get_accounting_data_for_valuation(self, cr, uid, move, context=None):
|
||||
# """
|
||||
# Return the accounts and journal to use to post Journal Entries for the real-time
|
||||
# valuation of the move.
|
||||
|
||||
# :param context: context dictionary that can explicitly mention the company to consider via the 'force_company' key
|
||||
# :raise: osv.except_osv() is any mandatory account or journal is not defined.
|
||||
# """
|
||||
# product_obj=self.pool.get('product.product')
|
||||
# accounts = product_obj.get_product_accounts(cr, uid, move.product_id.id, context)
|
||||
# if move.location_id.valuation_out_account_id:
|
||||
# acc_src = move.location_id.valuation_out_account_id.id
|
||||
# else:
|
||||
# acc_src = accounts['stock_account_input']
|
||||
|
||||
# if move.location_dest_id.valuation_in_account_id:
|
||||
# acc_dest = move.location_dest_id.valuation_in_account_id.id
|
||||
# else:
|
||||
# acc_dest = accounts['stock_account_output']
|
||||
|
||||
# acc_valuation = accounts.get('property_stock_valuation_account_id', False)
|
||||
# journal_id = accounts['stock_journal']
|
||||
|
||||
# if acc_dest == acc_valuation:
|
||||
# raise osv.except_osv(_('Error!'), _('Cannot create Journal Entry, Output Account of this product and Valuation account on category of this product are same.'))
|
||||
|
||||
# if acc_src == acc_valuation:
|
||||
# raise osv.except_osv(_('Error!'), _('Cannot create Journal Entry, Input Account of this product and Valuation account on category of this product are same.'))
|
||||
|
||||
# if not acc_src:
|
||||
# raise osv.except_osv(_('Error!'), _('Please define stock input account for this product or its category: "%s" (id: %d)') % \
|
||||
# (move.product_id.name, move.product_id.id,))
|
||||
# if not acc_dest:
|
||||
# raise osv.except_osv(_('Error!'), _('Please define stock output account for this product or its category: "%s" (id: %d)') % \
|
||||
# (move.product_id.name, move.product_id.id,))
|
||||
# if not journal_id:
|
||||
# raise osv.except_osv(_('Error!'), _('Please define journal on the product category: "%s" (id: %d)') % \
|
||||
# (move.product_id.categ_id.name, move.product_id.categ_id.id,))
|
||||
# if not acc_valuation:
|
||||
# raise osv.except_osv(_('Error!'), _('Please define inventory valuation account on the product category: "%s" (id: %d)') % \
|
||||
# (move.product_id.categ_id.name, move.product_id.categ_id.id,))
|
||||
# return journal_id, acc_src, acc_dest, acc_valuation
|
||||
|
||||
|
||||
##We can use a preliminary type
|
||||
#def get_reference_amount(self, cr, uid, move, qty, context=None):
|
||||
# # if product is set to average price and a specific value was entered in the picking wizard,
|
||||
# # we use it
|
||||
|
||||
# # by default the reference currency is that of the move's company
|
||||
# reference_currency_id = move.company_id.currency_id.id
|
||||
#
|
||||
# #I use
|
||||
# if move.product_id.cost_method != 'standard' and move.price_unit:
|
||||
# reference_amount = move.product_qty * move.price_unit #Using move.price_qty instead of qty to have correct amount
|
||||
# reference_currency_id = move.price_currency_id.id or reference_currency_id
|
||||
|
||||
# # Otherwise we default to the company's valuation price type, considering that the values of the
|
||||
# # valuation field are expressed in the default currency of the move's company.
|
||||
# else:
|
||||
# if context is None:
|
||||
# context = {}
|
||||
# currency_ctx = dict(context, currency_id = move.company_id.currency_id.id)
|
||||
# amount_unit = move.product_id.price_get('standard_price', context=currency_ctx)[move.product_id.id]
|
||||
# reference_amount = amount_unit * qty
|
||||
#
|
||||
# return reference_amount, reference_currency_id
|
||||
|
||||
|
||||
#def _get_reference_accounting_values_for_valuation(self, cr, uid, move, context=None):
|
||||
# """
|
||||
# Return the reference amount and reference currency representing the inventory valuation for this move.
|
||||
# These reference values should possibly be converted before being posted in Journals to adapt to the primary
|
||||
# and secondary currencies of the relevant accounts.
|
||||
# """
|
||||
# product_uom_obj = self.pool.get('product.uom')
|
||||
|
||||
# default_uom = move.product_id.uom_id.id
|
||||
# qty = product_uom_obj._compute_qty(cr, uid, move.product_uom.id, move.product_qty, default_uom)
|
||||
#
|
||||
# reference_amount, reference_currency_id = self.get_reference_amount(cr, uid, move, qty, context=context)
|
||||
# return reference_amount, reference_currency_id
|
||||
|
||||
|
||||
#
|
||||
|
||||
|
||||
#def _create_product_valuation_moves(self, cr, uid, move, matches, context=None):
|
||||
# """
|
||||
# Generate the appropriate accounting moves if the product being moved is subject
|
||||
# to real_time valuation tracking, and the source or the destination location is internal (not both)
|
||||
# This means an in or out move.
|
||||
#
|
||||
# Depending on the matches it will create the necessary moves
|
||||
# """
|
||||
# ctx = context.copy()
|
||||
# ctx['force_company'] = move.company_id.id
|
||||
# valuation = self.pool.get("product.product").browse(cr, uid, move.product_id.id, context=ctx).valuation
|
||||
# move_obj = self.pool.get('account.move')
|
||||
# if valuation == 'real_time':
|
||||
# if context is None:
|
||||
# context = {}
|
||||
# company_ctx = dict(context,force_company=move.company_id.id)
|
||||
# journal_id, acc_src, acc_dest, acc_valuation = self._get_accounting_data_for_valuation(cr, uid, move, context=company_ctx)
|
||||
# reference_amount, reference_currency_id = self._get_reference_accounting_values_for_valuation(cr, uid, move, context=company_ctx)
|
||||
# account_moves = []
|
||||
# # Outgoing moves (or cross-company output part)
|
||||
# if move.location_id.company_id \
|
||||
# and (move.location_id.usage == 'internal' and move.location_dest_id.usage != 'internal'):
|
||||
# #returning goods to supplier
|
||||
# if move.location_dest_id.usage == 'supplier':
|
||||
# account_moves += [(journal_id, self._create_account_move_line(cr, uid, move, matches, acc_valuation, acc_src, reference_amount, reference_currency_id, 'out', context=company_ctx))]
|
||||
# else:
|
||||
# account_moves += [(journal_id, self._create_account_move_line(cr, uid, move, matches, acc_valuation, acc_dest, reference_amount, reference_currency_id, 'out', context=company_ctx))]
|
||||
|
||||
# # Incoming moves (or cross-company input part)
|
||||
# if move.location_dest_id.company_id \
|
||||
# and (move.location_id.usage != 'internal' and move.location_dest_id.usage == 'internal'):
|
||||
# #goods return from customer
|
||||
# if move.location_id.usage == 'customer':
|
||||
# account_moves += [(journal_id, self._create_account_move_line(cr, uid, move, matches, acc_dest, acc_valuation, reference_amount, reference_currency_id, 'in', context=company_ctx))]
|
||||
# else:
|
||||
# account_moves += [(journal_id, self._create_account_move_line(cr, uid, move, matches, acc_src, acc_valuation, reference_amount, reference_currency_id, 'in', context=company_ctx))]
|
||||
# if matches and move.product_id.cost_method in ('fifo', 'lifo'):
|
||||
# outs = {}
|
||||
# match_obj = self.pool.get("stock.move.matching")
|
||||
# for match in match_obj.browse(cr, uid, matches, context=context):
|
||||
# if match.move_out_id.id in outs:
|
||||
# outs[match.move_out_id.id] += [match.id]
|
||||
# else:
|
||||
# outs[match.move_out_id.id] = [match.id]
|
||||
# #When in stock was negative, you will get matches for the in also:
|
||||
# account_moves_neg = []
|
||||
# for out_mov in self.browse(cr, uid, outs.keys(), context=context):
|
||||
# journal_id_out, acc_src_out, acc_dest_out, acc_valuation_out = self._get_accounting_data_for_valuation(cr, uid, out_mov, context=company_ctx)
|
||||
# reference_amount_out, reference_currency_id_out = self._get_reference_accounting_values_for_valuation(cr, uid, out_mov, context=company_ctx)
|
||||
# if out_mov.location_dest_id.usage == 'supplier':
|
||||
# # Is not the way it should be with acc_valuation
|
||||
# account_moves_neg += [(journal_id_out, self._create_account_move_line(cr, uid, out_mov, outs[out_mov.id], acc_valuation_out, acc_src_out, reference_amount_out, reference_currency_id_out, 'out', context=company_ctx))]
|
||||
# else:
|
||||
# account_moves_neg += [(journal_id_out, self._create_account_move_line(cr, uid, out_mov, outs[out_mov.id], acc_valuation_out, acc_dest_out, reference_amount_out, reference_currency_id_out, 'out', context=company_ctx))]
|
||||
# #Create account moves for outs which made stock go negative
|
||||
# for j_id, move_lines in account_moves_neg:
|
||||
# move_obj.create(cr, uid,
|
||||
# {'journal_id': j_id,
|
||||
# 'line_id': move_lines,
|
||||
# 'ref': out_mov.picking_id and out_mov.picking_id.name,
|
||||
# })
|
||||
# for j_id, move_lines in account_moves:
|
||||
# move_obj.create(cr, uid,
|
||||
# {
|
||||
# 'journal_id': j_id,
|
||||
# 'line_id': move_lines,
|
||||
# 'ref': move.picking_id and move.picking_id.name})
|
||||
|
||||
#def _create_account_move_line(self, cr, uid, quant, src_account_id, dest_account_id, context=None):
|
||||
# """
|
||||
# Generate the account.move.line values to post to track the stock valuation difference due to the
|
||||
# processing of the given stock move.
|
||||
# """
|
||||
# move_list = []
|
||||
# # Consists of access rights
|
||||
# # TODO Check if amount_currency is not needed
|
||||
# match_obj = self.pool.get("stock.move.matching")
|
||||
# if type == 'out' and move.product_id.cost_method in ['real']:
|
||||
# for match in match_obj.browse(cr, uid, matches, context=context):
|
||||
# move_list += [(match.qty, match.qty * match.price_unit_out)]
|
||||
# elif type == 'in' and move.product_id.cost_method in ['real']:
|
||||
# move_list = [(move.product_qty, reference_amount)]
|
||||
# else:
|
||||
# move_list = [(move.product_qty, reference_amount)]
|
||||
|
||||
# res = []
|
||||
# for item in move_list:
|
||||
# # prepare default values considering that the destination accounts have the reference_currency_id as their main currency
|
||||
# partner_id = (move.picking_id.partner_id and self.pool.get('res.partner')._find_accounting_partner(move.picking_id.partner_id).id) or False
|
||||
# debit_line_vals = {
|
||||
# 'name': move.name,
|
||||
# 'product_id': move.product_id and move.product_id.id or False,
|
||||
# 'quantity': item[0],
|
||||
# 'product_uom_id': move.product_uom.id,
|
||||
# 'ref': move.picking_id and move.picking_id.name or False,
|
||||
# 'date': time.strftime('%Y-%m-%d'),
|
||||
# 'partner_id': partner_id,
|
||||
# 'debit': item[1],
|
||||
# 'account_id': dest_account_id,
|
||||
# }
|
||||
# credit_line_vals = {
|
||||
# 'name': move.name,
|
||||
# 'product_id': move.product_id and move.product_id.id or False,
|
||||
# 'quantity': item[0],
|
||||
# 'product_uom_id': move.product_uom.id,
|
||||
# 'ref': move.picking_id and move.picking_id.name or False,
|
||||
# 'date': time.strftime('%Y-%m-%d'),
|
||||
# 'partner_id': partner_id,
|
||||
# 'credit': item[1],
|
||||
# 'account_id': src_account_id,
|
||||
# }
|
||||
# res += [(0, 0, debit_line_vals), (0, 0, credit_line_vals)]
|
||||
# return res
|
||||
|
||||
|
||||
#----------------------------------------------------------
|
||||
# Stock Picking
|
||||
#----------------------------------------------------------
|
||||
|
||||
class stock_picking(osv.osv):
|
||||
_inherit = "stock.picking"
|
||||
|
||||
_columns = {
|
||||
'invoice_state': fields.selection([
|
||||
("invoiced", "Invoiced"),
|
||||
("2binvoiced", "To Be Invoiced"),
|
||||
("none", "Not Applicable")], "Invoice Control",
|
||||
select=True, required=True, readonly=True, track_visibility='onchange', states={'draft': [('readonly', False)]}),
|
||||
}
|
||||
_defaults = {
|
||||
'invoice_state': 'none',
|
||||
}
|
||||
|
||||
def copy(self, cr, uid, id, default=None, context=None):
|
||||
if default is None:
|
||||
default = {}
|
||||
picking_obj = self.browse(cr, uid, id, context=context)
|
||||
if 'invoice_state' not in default and picking_obj.invoice_state == 'invoiced':
|
||||
default['invoice_state'] = '2binvoiced'
|
||||
return super(stock_picking, self).copy(cr, uid, id, default, context)
|
||||
|
||||
|
||||
def get_currency_id(self, cr, uid, picking):
|
||||
return False
|
||||
|
||||
def _get_partner_to_invoice(self, cr, uid, picking, context=None):
|
||||
""" Gets the partner that will be invoiced
|
||||
Note that this function is inherited in the sale and purchase modules
|
||||
@param picking: object of the picking for which we are selecting the partner to invoice
|
||||
@return: object of the partner to invoice
|
||||
"""
|
||||
return picking.partner_id and picking.partner_id.id
|
||||
|
||||
def _get_comment_invoice(self, cr, uid, picking):
|
||||
"""
|
||||
@return: comment string for invoice
|
||||
"""
|
||||
return picking.note or ''
|
||||
|
||||
def _get_price_unit_invoice(self, cr, uid, move_line, type, context=None):
|
||||
""" Gets price unit for invoice
|
||||
@param move_line: Stock move lines
|
||||
@param type: Type of invoice
|
||||
@return: The price unit for the move line
|
||||
"""
|
||||
if context is None:
|
||||
context = {}
|
||||
|
||||
if type in ('in_invoice', 'in_refund'):
|
||||
# Take the user company and pricetype
|
||||
context['currency_id'] = move_line.company_id.currency_id.id
|
||||
amount_unit = move_line.product_id.price_get('standard_price', context=context)[move_line.product_id.id]
|
||||
return amount_unit
|
||||
else:
|
||||
return move_line.product_id.list_price
|
||||
|
||||
def _get_discount_invoice(self, cr, uid, move_line):
|
||||
'''Return the discount for the move line'''
|
||||
return 0.0
|
||||
|
||||
def _get_taxes_invoice(self, cr, uid, move_line, type):
|
||||
""" Gets taxes on invoice
|
||||
@param move_line: Stock move lines
|
||||
@param type: Type of invoice
|
||||
@return: Taxes Ids for the move line
|
||||
"""
|
||||
if type in ('in_invoice', 'in_refund'):
|
||||
taxes = move_line.product_id.supplier_taxes_id
|
||||
else:
|
||||
taxes = move_line.product_id.taxes_id
|
||||
|
||||
if move_line.picking_id and move_line.picking_id.partner_id and move_line.picking_id.partner_id.id:
|
||||
return self.pool.get('account.fiscal.position').map_tax(
|
||||
cr,
|
||||
uid,
|
||||
move_line.picking_id.partner_id.property_account_position,
|
||||
taxes
|
||||
)
|
||||
else:
|
||||
return map(lambda x: x.id, taxes)
|
||||
|
||||
def _get_account_analytic_invoice(self, cr, uid, picking, move_line):
|
||||
return False
|
||||
|
||||
def _invoice_line_hook(self, cr, uid, move_line, invoice_line_id):
|
||||
'''Call after the creation of the invoice line'''
|
||||
return
|
||||
|
||||
def _invoice_hook(self, cr, uid, picking, invoice_id):
|
||||
'''Call after the creation of the invoice'''
|
||||
return
|
||||
|
||||
def _get_invoice_type(self, pick):
|
||||
src_usage = dest_usage = None
|
||||
inv_type = None
|
||||
if pick.invoice_state == '2binvoiced':
|
||||
if pick.move_lines:
|
||||
src_usage = pick.move_lines[0].location_id.usage
|
||||
dest_usage = pick.move_lines[0].location_dest_id.usage
|
||||
if pick.type == 'out' and dest_usage == 'supplier':
|
||||
inv_type = 'in_refund'
|
||||
elif pick.type == 'out' and dest_usage == 'customer':
|
||||
inv_type = 'out_invoice'
|
||||
elif pick.type == 'in' and src_usage == 'supplier':
|
||||
inv_type = 'in_invoice'
|
||||
elif pick.type == 'in' and src_usage == 'customer':
|
||||
inv_type = 'out_refund'
|
||||
else:
|
||||
inv_type = 'out_invoice'
|
||||
return inv_type
|
||||
|
||||
def _prepare_invoice_group(self, cr, uid, picking, partner, invoice, context=None):
|
||||
""" Builds the dict for grouped invoices
|
||||
@param picking: picking object
|
||||
@param partner: object of the partner to invoice (not used here, but may be usefull if this function is inherited)
|
||||
@param invoice: object of the invoice that we are updating
|
||||
@return: dict that will be used to update the invoice
|
||||
"""
|
||||
comment = self._get_comment_invoice(cr, uid, picking)
|
||||
return {
|
||||
'name': (invoice.name or '') + ', ' + (picking.name or ''),
|
||||
'origin': (invoice.origin or '') + ', ' + (picking.name or '') + (picking.origin and (':' + picking.origin) or ''),
|
||||
'comment': (comment and (invoice.comment and invoice.comment + "\n" + comment or comment)) or (invoice.comment and invoice.comment or ''),
|
||||
'date_invoice': context.get('date_inv', False),
|
||||
'user_id': uid,
|
||||
}
|
||||
|
||||
def _prepare_invoice(self, cr, uid, picking, partner, inv_type, journal_id, context=None):
|
||||
""" Builds the dict containing the values for the invoice
|
||||
@param picking: picking object
|
||||
@param partner: object of the partner to invoice
|
||||
@param inv_type: type of the invoice ('out_invoice', 'in_invoice', ...)
|
||||
@param journal_id: ID of the accounting journal
|
||||
@return: dict that will be used to create the invoice object
|
||||
"""
|
||||
if isinstance(partner, int):
|
||||
partner = self.pool.get('res.partner').browse(cr, uid, partner, context=context)
|
||||
if inv_type in ('out_invoice', 'out_refund'):
|
||||
account_id = partner.property_account_receivable.id
|
||||
payment_term = partner.property_payment_term.id or False
|
||||
else:
|
||||
account_id = partner.property_account_payable.id
|
||||
payment_term = partner.property_supplier_payment_term.id or False
|
||||
comment = self._get_comment_invoice(cr, uid, picking)
|
||||
invoice_vals = {
|
||||
'name': picking.name,
|
||||
'origin': (picking.name or '') + (picking.origin and (':' + picking.origin) or ''),
|
||||
'type': inv_type,
|
||||
'account_id': account_id,
|
||||
'partner_id': partner.id,
|
||||
'comment': comment,
|
||||
'payment_term': payment_term,
|
||||
'fiscal_position': partner.property_account_position.id,
|
||||
'date_invoice': context.get('date_inv', False),
|
||||
'company_id': picking.company_id.id,
|
||||
'user_id': uid,
|
||||
}
|
||||
cur_id = self.get_currency_id(cr, uid, picking)
|
||||
if cur_id:
|
||||
invoice_vals['currency_id'] = cur_id
|
||||
if journal_id:
|
||||
invoice_vals['journal_id'] = journal_id
|
||||
return invoice_vals
|
||||
|
||||
def _prepare_invoice_line(self, cr, uid, group, picking, move_line, invoice_id,
|
||||
invoice_vals, context=None):
|
||||
""" Builds the dict containing the values for the invoice line
|
||||
@param group: True or False
|
||||
@param picking: picking object
|
||||
@param: move_line: move_line object
|
||||
@param: invoice_id: ID of the related invoice
|
||||
@param: invoice_vals: dict used to created the invoice
|
||||
@return: dict that will be used to create the invoice line
|
||||
"""
|
||||
if group:
|
||||
name = (picking.name or '') + '-' + move_line.name
|
||||
else:
|
||||
name = move_line.name
|
||||
origin = move_line.picking_id.name or ''
|
||||
if move_line.picking_id.origin:
|
||||
origin += ':' + move_line.picking_id.origin
|
||||
|
||||
if invoice_vals['type'] in ('out_invoice', 'out_refund'):
|
||||
account_id = move_line.product_id.property_account_income.id
|
||||
if not account_id:
|
||||
account_id = move_line.product_id.categ_id.\
|
||||
property_account_income_categ.id
|
||||
else:
|
||||
account_id = move_line.product_id.property_account_expense.id
|
||||
if not account_id:
|
||||
account_id = move_line.product_id.categ_id.\
|
||||
property_account_expense_categ.id
|
||||
if invoice_vals['fiscal_position']:
|
||||
fp_obj = self.pool.get('account.fiscal.position')
|
||||
fiscal_position = fp_obj.browse(cr, uid, invoice_vals['fiscal_position'], context=context)
|
||||
account_id = fp_obj.map_account(cr, uid, fiscal_position, account_id)
|
||||
# set UoS if it's a sale and the picking doesn't have one
|
||||
uos_id = move_line.product_uos and move_line.product_uos.id or False
|
||||
if not uos_id and invoice_vals['type'] in ('out_invoice', 'out_refund'):
|
||||
uos_id = move_line.product_uom.id
|
||||
|
||||
return {
|
||||
'name': name,
|
||||
'origin': origin,
|
||||
'invoice_id': invoice_id,
|
||||
'uos_id': uos_id,
|
||||
'product_id': move_line.product_id.id,
|
||||
'account_id': account_id,
|
||||
'price_unit': self._get_price_unit_invoice(cr, uid, move_line, invoice_vals['type']),
|
||||
'discount': self._get_discount_invoice(cr, uid, move_line),
|
||||
'quantity': move_line.product_uos_qty or move_line.product_qty,
|
||||
'invoice_line_tax_id': [(6, 0, self._get_taxes_invoice(cr, uid, move_line, invoice_vals['type']))],
|
||||
'account_analytic_id': self._get_account_analytic_invoice(cr, uid, picking, move_line),
|
||||
}
|
||||
|
||||
def action_invoice_create(self, cr, uid, ids, journal_id=False,
|
||||
group=False, type='out_invoice', context=None):
|
||||
""" Creates invoice based on the invoice state selected for picking.
|
||||
@param journal_id: Id of journal
|
||||
@param group: Whether to create a group invoice or not
|
||||
@param type: Type invoice to be created
|
||||
@return: Ids of created invoices for the pickings
|
||||
"""
|
||||
if context is None:
|
||||
context = {}
|
||||
|
||||
invoice_obj = self.pool.get('account.invoice')
|
||||
invoice_line_obj = self.pool.get('account.invoice.line')
|
||||
partner_obj = self.pool.get('res.partner')
|
||||
invoices_group = {}
|
||||
res = {}
|
||||
inv_type = type
|
||||
for picking in self.browse(cr, uid, ids, context=context):
|
||||
if picking.invoice_state != '2binvoiced':
|
||||
continue
|
||||
partner = self._get_partner_to_invoice(cr, uid, picking, context=context)
|
||||
if isinstance(partner, int):
|
||||
partner = partner_obj.browse(cr, uid, [partner], context=context)[0]
|
||||
if not partner:
|
||||
raise osv.except_osv(_('Error, no partner!'),
|
||||
_('Please put a partner on the picking list if you want to generate invoice.'))
|
||||
|
||||
if not inv_type:
|
||||
inv_type = self._get_invoice_type(picking)
|
||||
|
||||
if group and partner.id in invoices_group:
|
||||
invoice_id = invoices_group[partner.id]
|
||||
invoice = invoice_obj.browse(cr, uid, invoice_id)
|
||||
invoice_vals_group = self._prepare_invoice_group(cr, uid, picking, partner, invoice, context=context)
|
||||
invoice_obj.write(cr, uid, [invoice_id], invoice_vals_group, context=context)
|
||||
else:
|
||||
invoice_vals = self._prepare_invoice(cr, uid, picking, partner, inv_type, journal_id, context=context)
|
||||
invoice_id = invoice_obj.create(cr, uid, invoice_vals, context=context)
|
||||
invoices_group[partner.id] = invoice_id
|
||||
res[picking.id] = invoice_id
|
||||
for move_line in picking.move_lines:
|
||||
if move_line.state == 'cancel':
|
||||
continue
|
||||
if move_line.scrapped:
|
||||
# do no invoice scrapped products
|
||||
continue
|
||||
vals = self._prepare_invoice_line(cr, uid, group, picking, move_line,
|
||||
invoice_id, invoice_vals, context=context)
|
||||
if vals:
|
||||
invoice_line_id = invoice_line_obj.create(cr, uid, vals, context=context)
|
||||
self._invoice_line_hook(cr, uid, move_line, invoice_line_id)
|
||||
|
||||
invoice_obj.button_compute(cr, uid, [invoice_id], context=context,
|
||||
set_total=(inv_type in ('in_invoice', 'in_refund')))
|
||||
self.write(cr, uid, [picking.id], {
|
||||
'invoice_state': 'invoiced',
|
||||
}, context=context)
|
||||
self._invoice_hook(cr, uid, picking, invoice_id)
|
||||
self.write(cr, uid, res.keys(), {
|
||||
'invoice_state': 'invoiced',
|
||||
}, context=context)
|
||||
return res
|
||||
|
||||
|
||||
|
||||
|
||||
# FP Note: review all methods above this line for stock.picking
|
||||
|
||||
# ----------------------------------------------------
|
||||
# Move
|
||||
# ----------------------------------------------------
|
||||
|
||||
class stock_move(osv.osv):
|
||||
_name = "stock.move"
|
||||
|
||||
#TODO cancel a move must delete the accounting entry if not posted yet (otherwise raise an error)
|
||||
def action_cancel(self, cr, uid, ids, context=None):
|
||||
super(stock_move, self).action_cancel(cr, uid, ids, context=context)
|
||||
|
||||
#class stock_inventory(osv.osv):
|
||||
# _name = "stock.inventory"
|
||||
#
|
||||
# def action_cancel_draft(self, cr, uid, ids, context=None):
|
||||
# """ Cancels the stock move and change inventory state to draft.
|
||||
# @return: True
|
||||
# """
|
||||
# for inv in self.browse(cr, uid, ids, context=context):
|
||||
# self.pool.get('stock.move').action_cancel(cr, uid, [x.id for x in inv.move_ids], context=context)
|
||||
# self.write(cr, uid, [inv.id], {'state':'draft'}, context=context)
|
||||
# return True
|
||||
#
|
||||
# def action_cancel_inventory(self, cr, uid, ids, context=None):
|
||||
# """ Cancels both stock move and inventory
|
||||
# @return: True
|
||||
# """
|
||||
# move_obj = self.pool.get('stock.move')
|
||||
# account_move_obj = self.pool.get('account.move')
|
||||
# for inv in self.browse(cr, uid, ids, context=context):
|
||||
# move_obj.action_cancel(cr, uid, [x.id for x in inv.move_ids], context=context)
|
||||
# for move in inv.move_ids:
|
||||
# account_move_ids = account_move_obj.search(cr, uid, [('name', '=', move.name)])
|
||||
# if account_move_ids:
|
||||
# account_move_data_l = account_move_obj.read(cr, uid, account_move_ids, ['state'], context=context)
|
||||
# for account_move in account_move_data_l:
|
||||
# if account_move['state'] == 'posted':
|
||||
# raise osv.except_osv(_('User Error!'),
|
||||
# _('In order to cancel this inventory, you must first unpost related journal entries.'))
|
||||
# account_move_obj.unlink(cr, uid, [account_move['id']], context=context)
|
||||
# self.write(cr, uid, [inv.id], {'state': 'cancel'}, context=context)
|
||||
# return True
|
||||
|
||||
|
||||
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data noupdate="1">
|
||||
|
||||
<record id="stock_journal_sequence" model="ir.sequence">
|
||||
<field name="name">Stock Journal Sequence</field>
|
||||
<field eval="3" name="padding"/>
|
||||
<field name="prefix">STJ/%(year)s/</field>
|
||||
</record>
|
||||
<record forcecreate="1" id="stock_journal" model="account.journal">
|
||||
<field name="name">Stock Journal</field>
|
||||
<field name="code">STJ</field>
|
||||
<field name="type">general</field>
|
||||
<field name="sequence_id" ref="stock_journal_sequence"/>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data noupdate="1">
|
||||
|
||||
<record forcecreate="True" id="property_stock_valuation_account_id" model="ir.property">
|
||||
<field name="name">property_stock_valuation_account_id</field>
|
||||
<field name="fields_id" search="[('model','=','product.category'),('name','=','property_stock_valuation_account_id')]"/>
|
||||
<field eval="'account.account,'+str(ref('account.stk'))" model="account.account" name="value"/>
|
||||
<field name="company_id" ref="base.main_company"/>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
|
||||
|
||||
<record id="view_location_form_inherit" model="ir.ui.view">
|
||||
<field name="name">stock.location.form.inherit</field>
|
||||
<field name="model">stock.location</field>
|
||||
<field name="inherit_id" ref="stock.stock_location_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//group[@name='localization']" position="after">
|
||||
<group string="Accounting Information" attrs="{'invisible':[('usage','not in',('inventory','production'))]}">
|
||||
<field name="valuation_in_account_id"/>
|
||||
<field name="valuation_out_account_id"/>
|
||||
</group>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_picking_inherit_form" model="ir.ui.view">
|
||||
<field name="name">stock.picking.form.inherit</field>
|
||||
<field name="model">stock.picking</field>
|
||||
<field eval="12" name="priority"/>
|
||||
<field name="inherit_id" ref="stock.view_picking_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//bouton[@name='action_process']" position="after">
|
||||
<button name="%(action_stock_invoice_onshipping)d" string="Create Invoice/Refund" attrs="{'invisible': ['|','|',('state','<>','done'),('invoice_state','=','invoiced'),('invoice_state','=','none')]}" type="action" class="oe_highlight" groups="base.group_user"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='back_order_id']" position="after">
|
||||
<field name="invoice_state" string="Invoice Control" groups="account.group_account_invoice" attrs="{'invisible':[('invoice_state', '=', 'none')]}"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
|
||||
<record id="view_picking_out_tree_inherit" model="ir.ui.view">
|
||||
<field name="name">stock.picking.out.tree.inherit</field>
|
||||
<field name="model">stock.picking.out</field>
|
||||
<field name="inherit_id" ref="stock.view_picking_out_tree"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='min_date']" position="after">
|
||||
<field name="invoice_state" groups="account.group_account_invoice"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
<record id="view_picking_in_tree_inherit" model="ir.ui.view">
|
||||
<field name="name">stock.picking.in.tree.inherit</field>
|
||||
<field name="model">stock.picking.in</field>
|
||||
<field name="inherit_id" ref="stock.view_picking_in_tree"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='min_date']" position="after">
|
||||
<field name="invoice_state" groups="account.group_account_invoice"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="vpick_tree_inherit" model="ir.ui.view">
|
||||
<field name="name">stock.picking.tree.inherit</field>
|
||||
<field name="model">stock.picking.in</field>
|
||||
<field name="inherit_id" ref="stock.vpick_tree"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='min_date']" position="after">
|
||||
<field name="invoice_state" groups="account.group_account_invoice"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
<record id="view_picking_in_search_inherit" model="ir.ui.view">
|
||||
<field name="name">stock.picking.in.search.inherit</field>
|
||||
<field name="model">stock.picking.in</field>
|
||||
<field name="inherit_id" ref="stock.view_picking_in_search"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='partner_id']" position="before">
|
||||
<filter string="To Invoice" name="to_invoice" icon="terp-dolar" domain="[('invoice_state', '=', '2binvoiced')]"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
<record id="view_picking_out_search_inherit" model="ir.ui.view">
|
||||
<field name="name">stock.picking.out.search.inherit</field>
|
||||
<field name="model">stock.picking</field>
|
||||
<field name="inherit_id" ref="stock.view_picking_out_search"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='partner_id']" position="before">
|
||||
<filter icon="terp-dolar" name="to_invoice" string="To Invoice" domain="[('invoice_state','=','2binvoiced')]" help="Delivery orders to invoice"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
Loading…
Reference in New Issue