[IMP] Barcode interface and transfer wizard in picking form instead
Pack operations will not appear in a transfer wizard or bar code interface anymore, but in the picking form itself. Some wizards are still needed however to ask whether a backorder needs to be created and whether we need to do an immediate transfer The biggest change in the model is in the picking where the locations of the picking are not a related anymore, but need to be set. That is why the tests need to be changed. This allows also to have pickings without moves initially and just move one pack e.g. Locations won't be visible on stock moves in a picking either. (as they should be those of the picking) In order to avoid confusion, the pack operations are created immediately when stock is reserved and we are not waiting until the wizard or the bar code interface is launched anymore. The recompute button previously in the barcode interface will only appear when the reservation changed and the user indicated in the pack operations that something was moved already. Otherwise it will recompute the pack operations. That way the reservation on the stock moves, is reflected by the pack operations. Return pickings are not confirmed automatically when confirming the return wizard. That should make it possible to choose another location for the return. (Don't return in output e.g.) Barcode scanning is not in this commit.
This commit is contained in:
@@ -14,8 +14,8 @@
|
||||
<field name="model">stock.picking</field>
|
||||
<field name="inherit_id" ref="stock.view_picking_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//button[@name='do_partial_open_barcode']" position="before">
|
||||
<button class="oe_inline oe_stat_button" type="action" attrs="{'invisible': [('picking_type_code', '!=', 'outgoing')]}"
|
||||
<xpath expr="//div[@name='button_box']" position="inside">
|
||||
<button class="oe_stat_button" type="action" attrs="{'invisible': [('picking_type_code', '!=', 'outgoing')]}"
|
||||
name="%(action_claim_from_delivery)d" icon="fa-comments" >
|
||||
<field string="Claims" name="claim_count_out" widget="statinfo"/>
|
||||
</button>
|
||||
|
||||
@@ -717,7 +717,7 @@
|
||||
<header>
|
||||
<button name="button_confirm" states="draft" string="Confirm Production" class="oe_highlight"/>
|
||||
<button name="%(act_mrp_product_produce)d" states="ready,in_production" string="Produce" type="action" class="oe_highlight"/>
|
||||
<button name="action_assign" states="confirmed,picking_except" string="Check Availability" type="object" class="oe_highlight"/>
|
||||
<button name="action_assign" states="confirmed,picking_except" string="Reserve" type="object" class="oe_highlight"/>
|
||||
<button name="force_production" states="confirmed" string="Force Reservation" type="object"/>
|
||||
<button name="button_produce" states="ready" string="Mark as Started"/>
|
||||
<button name="button_cancel" states="draft,ready,in_production" string="Cancel Production"/>
|
||||
|
||||
@@ -796,6 +796,15 @@ class pos_order(osv.osv):
|
||||
addr = order.partner_id and partner_obj.address_get(cr, uid, [order.partner_id.id], ['delivery']) or {}
|
||||
picking_type = order.picking_type_id
|
||||
picking_id = False
|
||||
location_id = order.location_id.id
|
||||
if order.partner_id:
|
||||
destination_id = order.partner_id.property_stock_customer.id
|
||||
elif picking_type:
|
||||
if not picking_type.default_location_dest_id:
|
||||
raise UserError(_('Missing source or destination location for picking type %s. Please configure those fields and try again.' % (picking_type.name,)))
|
||||
destination_id = picking_type.default_location_dest_id.id
|
||||
else:
|
||||
destination_id = partner_obj.default_get(cr, uid, ['property_stock_customer'], context=context)['property_stock_customer']
|
||||
if picking_type:
|
||||
picking_id = picking_obj.create(cr, uid, {
|
||||
'origin': order.name,
|
||||
@@ -806,17 +815,10 @@ class pos_order(osv.osv):
|
||||
'move_type': 'direct',
|
||||
'note': order.note or "",
|
||||
'invoice_state': 'none',
|
||||
'location_id': location_id,
|
||||
'location_dest_id': destination_id,
|
||||
}, context=context)
|
||||
self.write(cr, uid, [order.id], {'picking_id': picking_id}, context=context)
|
||||
location_id = order.location_id.id
|
||||
if order.partner_id:
|
||||
destination_id = order.partner_id.property_stock_customer.id
|
||||
elif picking_type:
|
||||
if not picking_type.default_location_dest_id:
|
||||
raise UserError(_('Missing source or destination location for picking type %s. Please configure those fields and try again.' % (picking_type.name,)))
|
||||
destination_id = picking_type.default_location_dest_id.id
|
||||
else:
|
||||
destination_id = partner_obj.default_get(cr, uid, ['property_stock_customer'], context=context)['property_stock_customer']
|
||||
|
||||
move_list = []
|
||||
for line in order.lines:
|
||||
|
||||
@@ -834,7 +834,9 @@ class purchase_order(osv.osv):
|
||||
'picking_type_id': order.picking_type_id.id,
|
||||
'partner_id': order.partner_id.id,
|
||||
'date': order.date_order,
|
||||
'origin': order.name
|
||||
'origin': order.name,
|
||||
'location_id': order.partner_id.property_stock_supplier.id,
|
||||
'location_dest_id': order.location_id.id,
|
||||
}
|
||||
picking_id = self.pool.get('stock.picking').create(cr, uid, picking_vals, context=context)
|
||||
self._create_stock_moves(cr, uid, order, order.order_line, picking_id, context=context)
|
||||
|
||||
@@ -72,7 +72,11 @@
|
||||
pick_ids = self.pool.get('purchase.order').browse(cr, uid, ref("purchase_order_fiforet2")).picking_ids
|
||||
return_id = self.create(cr, uid, {}, context={'active_model':'stock.picking', 'active_id': pick_ids[0].id})
|
||||
return_picking_id, dummy = self._create_returns(cr, uid, [return_id], context={'active_id': pick_ids[0].id})
|
||||
self.pool.get('stock.picking').do_transfer(cr, uid, [return_picking_id])
|
||||
# Important to pass through confirmation and assignation
|
||||
pick_obj = self.pool.get('stock.picking')
|
||||
pick_obj.action_confirm(cr, uid, [return_picking_id])
|
||||
pick_obj.action_assign(cr, uid, [return_picking_id])
|
||||
pick_obj.do_transfer(cr, uid, [return_picking_id])
|
||||
-
|
||||
Check the standard price of the product changed to 80.0 as we returned the quants of purchase order 2
|
||||
-
|
||||
|
||||
@@ -57,7 +57,7 @@ Dashboard / Reports for Warehouse Management will include:
|
||||
'wizard/stock_return_picking_view.xml',
|
||||
'wizard/make_procurement_view.xml',
|
||||
'wizard/orderpoint_procurement_view.xml',
|
||||
'wizard/stock_transfer_details.xml',
|
||||
'wizard/stock_pack_details.xml',
|
||||
'stock_incoterms.xml',
|
||||
'stock_report.xml',
|
||||
'stock_view.xml',
|
||||
@@ -72,16 +72,16 @@ Dashboard / Reports for Warehouse Management will include:
|
||||
'views/report_stockpicking.xml',
|
||||
'views/report_stockpicking_operations.xml',
|
||||
'views/report_stockinventory.xml',
|
||||
'views/stock.xml',
|
||||
'report/report_stock_forecast.xml',
|
||||
'stock_dashboard.xml',
|
||||
'wizard/stock_immediate_transfer.xml',
|
||||
'wizard/stock_backorder_confirmation.xml'
|
||||
],
|
||||
'test': [
|
||||
'test/inventory.yml',
|
||||
'test/move.yml',
|
||||
'test/procrule.yml',
|
||||
'test/stock_users.yml',
|
||||
'stock_demo.yml',
|
||||
'test/shipment.yml',
|
||||
'test/packing.yml',
|
||||
'test/packingneg.yml',
|
||||
@@ -90,5 +90,4 @@ Dashboard / Reports for Warehouse Management will include:
|
||||
'installable': True,
|
||||
'application': True,
|
||||
'auto_install': False,
|
||||
'qweb': ['static/src/xml/picking.xml'],
|
||||
}
|
||||
|
||||
@@ -149,16 +149,25 @@ class procurement_order(osv.osv):
|
||||
old_proc = False
|
||||
key = tuple()
|
||||
key_routes = {}
|
||||
proc = False
|
||||
for proc, route in product_routes:
|
||||
key += (route,)
|
||||
if old_proc != proc:
|
||||
if not old_proc:
|
||||
old_proc = proc
|
||||
if old_proc == proc:
|
||||
key += (route,)
|
||||
else:
|
||||
if key:
|
||||
if key_routes.get(key):
|
||||
key_routes[key] += [proc]
|
||||
key_routes[key] += [old_proc]
|
||||
else:
|
||||
key_routes[key] = [proc]
|
||||
key_routes[key] = [old_proc]
|
||||
old_proc = proc
|
||||
key = tuple()
|
||||
key = (route,)
|
||||
if proc: #do not forget last one as we passed through it
|
||||
if key_routes.get(key):
|
||||
key_routes[key] += [proc]
|
||||
else:
|
||||
key_routes[key] = [proc]
|
||||
return key_routes
|
||||
|
||||
|
||||
@@ -193,7 +202,6 @@ class procurement_order(osv.osv):
|
||||
if res and res[0]:
|
||||
results_dict[procurement.id] = res[0]
|
||||
|
||||
|
||||
procurements_to_check = [x for x in procurements if x.id not in results_dict.keys()]
|
||||
#group by warehouse_id:
|
||||
wh_dict = self._get_wh_loc_dict(cr, uid, procurements_to_check, context=context)
|
||||
|
||||
@@ -65,7 +65,7 @@ access_procurement_rule,procurement.rule.flow,model_procurement_rule,base.group_
|
||||
access_procurement_rule_internal,procurement.rule.flow internal,model_procurement_rule,base.group_user,1,0,0,0
|
||||
access_stock_pack_operation_manager,stock.pack.operation manager,model_stock_pack_operation,stock.group_stock_manager,1,1,1,1
|
||||
access_stock_pack_operation_user,stock.pack.operation user,model_stock_pack_operation,stock.group_stock_user,1,1,1,1
|
||||
access_stock_pack_operation_all,stock.pack.operation all users,model_stock_pack_operation,base.group_user,1,0,0,0
|
||||
access_stock_pack_operation_all,stock.pack.operation all users,model_stock_pack_operation,base.group_user,1,1,1,1
|
||||
access_product_putaway_all,product.putaway all users,model_product_putaway,base.group_user,1,0,0,0
|
||||
access_product_putaway_manager,product.putaway all managers,model_product_putaway,stock.group_stock_manager,1,1,1,1
|
||||
access_stock_removal_all,product.removal all users,model_product_removal,base.group_user,1,0,0,0
|
||||
@@ -73,7 +73,7 @@ access_stock_fixed_putaway_strat,stock_fixed_putaway_strat managers,model_stock_
|
||||
access_stock_fixed_putaway_user,stock_fixed_putaway_strat user,model_stock_fixed_putaway_strat,stock.group_stock_user,1,0,0,0
|
||||
access_stock_move_operation_link_manager,stock.move.operation.link manager,model_stock_move_operation_link,stock.group_stock_manager,1,1,1,1
|
||||
access_stock_move_operation_link_user,stock.move.operation.link user,model_stock_move_operation_link,stock.group_stock_user,1,1,1,1
|
||||
access_stock_move_operation_link_all,stock.move.operation.link all users,model_stock_move_operation_link,base.group_user,1,0,0,0
|
||||
access_stock_move_operation_link_all,stock.move.operation.link all users,model_stock_move_operation_link,base.group_user,1,1,1,1
|
||||
access_product_price_history_stock_user,prices.history stock user,product.model_product_price_history,stock.group_stock_user,1,0,1,0
|
||||
access_product_price_history_stock_manager,prices.history stock manager,product.model_product_price_history,stock.group_stock_manager,1,1,1,1
|
||||
access_barcode_nomenclature_stock_user,barcode.nomenclature.stock.user,barcodes.model_barcode_nomenclature,stock.group_stock_user,1,0,0,0
|
||||
|
||||
|
@@ -1,134 +0,0 @@
|
||||
.in_container_hidden {
|
||||
display: none;
|
||||
}
|
||||
.in_container {
|
||||
}
|
||||
.oe_pick_app_header{
|
||||
margin-top: 0;
|
||||
}
|
||||
.oe_picking {
|
||||
cursor: pointer;
|
||||
}
|
||||
.oe_kanban.oe_picking {
|
||||
min-height: 80px;
|
||||
margin-bottom: 10px;
|
||||
border: 1px solid rgba(0, 0, 0, 0.16);
|
||||
border-bottom-color: rgba(0, 0, 0, 0.3);
|
||||
-webkit-transition: -webkit-transform, -webkit-box-shadow, border 200ms linear;
|
||||
-moz-border-radius: 4px;
|
||||
-webkit-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.oe_kanban_color_0 {
|
||||
background-color: white;
|
||||
color: #5a5a5a;
|
||||
}
|
||||
.oe_kanban_color_1 {
|
||||
background-color: #cccccc;
|
||||
color: #424242;
|
||||
}
|
||||
.oe_kanban_color_2 {
|
||||
background-color: #ffc7c7;
|
||||
color: #7a3737;
|
||||
}
|
||||
.oe_kanban_color_3 {
|
||||
background-color: #fff1c7;
|
||||
color: #756832;
|
||||
}
|
||||
.oe_kanban_color_4 {
|
||||
background-color: #e3ffc7;
|
||||
color: #5d6937;
|
||||
}
|
||||
.oe_kanban_color_5 {
|
||||
background-color: #c7ffd5;
|
||||
color: #1a7759;
|
||||
}
|
||||
.oe_kanban_color_6 {
|
||||
background-color: #c7ffff;
|
||||
color: #1a5d83;
|
||||
}
|
||||
.oe_kanban_color_7 {
|
||||
background-color: #c7d5ff;
|
||||
color: #3b3e75;
|
||||
}
|
||||
.oe_kanban_color_8 {
|
||||
background-color: #e3c7ff;
|
||||
color: #4c3668;
|
||||
}
|
||||
.oe_kanban_color_9 {
|
||||
background-color: #ffc7f1;
|
||||
color: #6d2c70;
|
||||
}
|
||||
|
||||
/*Blinking text*/
|
||||
.blink_me {
|
||||
-webkit-animation-name: blinker;
|
||||
-webkit-animation-duration: 1s;
|
||||
-webkit-animation-timing-function: linear;
|
||||
-webkit-animation-iteration-count: 2;
|
||||
|
||||
-moz-animation-name: blinker;
|
||||
-moz-animation-duration: 1s;
|
||||
-moz-animation-timing-function: linear;
|
||||
-moz-animation-iteration-count: 2;
|
||||
|
||||
animation-name: blinker;
|
||||
animation-duration: 1s;
|
||||
animation-timing-function: linear;
|
||||
animation-iteration-count: 2;
|
||||
}
|
||||
|
||||
@-moz-keyframes blinker {
|
||||
0% { opacity: 1.0; }
|
||||
50% { opacity: 0.0; }
|
||||
100% { opacity: 1.0; }
|
||||
}
|
||||
|
||||
@-webkit-keyframes blinker {
|
||||
0% { opacity: 1.0; }
|
||||
50% { opacity: 0.0; }
|
||||
100% { opacity: 1.0; }
|
||||
}
|
||||
|
||||
@keyframes blinker {
|
||||
0% { opacity: 1.0; }
|
||||
50% { opacity: 0.0; }
|
||||
100% { opacity: 1.0; }
|
||||
}
|
||||
|
||||
/*hide OpenERP leftbar, table should use all width by default and display vertical scrollbar if needed*/
|
||||
.oe_leftbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
table.oe_webclient.oe_content_full_screen{
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
body{
|
||||
overflow-y: visible !important;
|
||||
}
|
||||
|
||||
/* --- Styling of OpenERP Elements ---
|
||||
Needed for loading and error box */
|
||||
|
||||
/* Increase z-index value to insure that loading goes above navbar of bootstrap*/
|
||||
.openerp .oe_loading {
|
||||
display: none;
|
||||
z-index: 1000;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 50%;
|
||||
padding: 4px 12px;
|
||||
background: #a61300;
|
||||
color: white;
|
||||
text-align: center;
|
||||
border: 1px solid #990000;
|
||||
border-top: none;
|
||||
-moz-border-radius-bottomright: 8px;
|
||||
-moz-border-radius-bottomleft: 8px;
|
||||
border-bottom-right-radius: 8px;
|
||||
border-bottom-left-radius: 8px;
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
.openerp .oe_kanban_view .oe_kanban_stock_picking_type {
|
||||
width: 345px;
|
||||
cursor: default;
|
||||
min-height: 245px !important;
|
||||
}
|
||||
.openerp .oe_stock_scan_image {
|
||||
opacity: 0.2;
|
||||
margin: 0 5px 0;
|
||||
}
|
||||
.openerp .oe_stock_scan_image:hover {
|
||||
opacity: 1
|
||||
}
|
||||
|
||||
.oe_stock_scan_image_btn {
|
||||
/*height : 42px;*/
|
||||
}
|
||||
.oe_stock_scan_button {
|
||||
border: none !important;
|
||||
background: none !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 5.3 KiB |
File diff suppressed because it is too large
Load Diff
@@ -1,6 +0,0 @@
|
||||
.oe_kanban_view.o_kanban_dashboard.o_stock_kanban {
|
||||
.oe_kanban_record {
|
||||
max-width: 500px;
|
||||
min-width: 350px;
|
||||
}
|
||||
}
|
||||
@@ -1,274 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates id="template" xml:space="preserve">
|
||||
|
||||
<t t-name='PickingEditorWidget'>
|
||||
|
||||
<div class="modal fade" id="js_LocationChooseModal" tabindex="-1" role="dialog" aria-labelledby="LocationChooseModal" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">x</button>
|
||||
<h4 class="modal-title" id="myModalLabel">Choose a location</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>Scan a location or select it in the list below</p>
|
||||
<select id="js_loc_select" class="form-control">
|
||||
<option class="js_loc_option" data-loc-id="false"></option>
|
||||
<t t-foreach="widget.get_location()" t-as="loc">
|
||||
<option class="js_loc_option" t-att-data-loc-id="loc.id"><t t-esc="loc.name"/></option>
|
||||
</t>
|
||||
</select>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||
<button type="button" class="btn btn-primary js_validate_location">Change Location</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="js_LotChooseModal" tabindex="-1" role="dialog" aria-labelledby="LotChooseModal" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">x</button>
|
||||
<h4 class="modal-title" id="myModalLabel">Create Lot</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>Scan a lot or type it below (leave empty to generate one automatically)</p>
|
||||
<input class='col-xs-6 js_lot_scan' type='text'/>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||
<button type="button" class="btn btn-primary js_validate_lot">Create Lot</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="js_PackConfModal" tabindex="-1" role="dialog" aria-labelledby="PackConfModal" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">x</button>
|
||||
<h4 class="modal-title" id="myModalLabel">Configure package</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>Package type</p>
|
||||
<select id="js_packconf_select" class="form-control">
|
||||
<option class="js_packing_option" data-ul-id="false"></option>
|
||||
<t t-foreach="widget.get_logisticunit()" t-as="ul">
|
||||
<option class="js_packing_option" t-att-data-ul-id="ul.id" t-att-value="ul.id"><t t-esc="ul.name"/></option>
|
||||
</t>
|
||||
</select>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||
<button type="button" class="btn btn-primary js_validate_pack">Validate package</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div>
|
||||
<div class="col-sm-4 col-xs-6">
|
||||
<h2 class="oe_pick_app_header" />
|
||||
</div>
|
||||
<div class="col-sm-8 col-xs⁻6 text-right">
|
||||
|
||||
<button type="button" class='btn btn-default js_pick_done'> Create backorder </button>
|
||||
<button type="button" class='btn btn-default js_pick_print'> Print </button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div>
|
||||
<div class="col-md-3 col-sm-4 col-xs-6">
|
||||
<h3><strong>
|
||||
<select id="js_select" class="form-control">
|
||||
<option value="ToDo" id="js_select_todo">Operations ToDo</option>
|
||||
<option value="Processed" id="js_select_processed">Operations Processed</option>
|
||||
</select>
|
||||
</strong>
|
||||
</h3>
|
||||
</div>
|
||||
<div class="col-md-3 col-sm-4 col-xs-6">
|
||||
<h2>
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control oe_searchbox" placeholder="Filter by location..."/>
|
||||
<span class="input-group-btn">
|
||||
<button class="btn btn-danger js_clear_search" type="button">x</button>
|
||||
</span>
|
||||
</div>
|
||||
</h2>
|
||||
</div>
|
||||
<div class="col-md-6 col-sm-4 col-xs-12 text-right">
|
||||
<h3>
|
||||
<button type="button" class='btn btn-default js_pick_pack js_putinpack'> Put in Pack </button>
|
||||
<button type="button" class='btn btn-danger js_drop_down fa fa-arrow-circle-o-down'> Put in Cart </button>
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<table class='table table-condensed js_op_table_todo'>
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-left">Product</th>
|
||||
<th class='text-center' width="150">Scanned</th>
|
||||
<th class='text-center'>Todo</th>
|
||||
<th>From</th>
|
||||
<th>To</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<t t-foreach="widget.get_rows()" t-as="row">
|
||||
<tr t-att-class="row.classes + 'js_pack_op_line'" t-att-data-id="row.cols.id" t-att-data-container-id="row.cols.container_id" t-att-data-package-id="row.cols.package_id" t-att-data-ulid="row.cols.ul_id">
|
||||
<td t-att-class="'brctbl-col1 text-left' + row.cols.head_container ? ' js_unfold' : ''">
|
||||
<t t-if="!row.cols.head_container && row.cols.container"><span class="fa fa-level-up fa-rotate-90" style="margin-left:10px;margin-right:10px;"></span></t>
|
||||
<t t-esc="row.cols.product" />
|
||||
</td>
|
||||
<td class='brctbl-col2 text-center js_row_qty'>
|
||||
<t t-if="row.cols.processed == 'false' && !row.cols.container">
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon js_minus input-sm" t-att-data-product-id='row.cols.product_id'><a href="#"><i class="fa fa-minus"></i></a></span>
|
||||
<form class="js_submit_value">
|
||||
<input type="text" class="form-control text-center js_qty" t-att-value="row.cols.rem"></input>
|
||||
<!-- <input type="submit" class="hidden"></input> -->
|
||||
</form>
|
||||
<span class="input-group-addon js_plus input-sm" t-att-data-product-id='row.cols.product_id'><a href="#"><i class="fa fa-plus"></i></a></span>
|
||||
</div>
|
||||
</t>
|
||||
<t t-if="(row.cols.processed == 'true' || row.cols.container)">
|
||||
<t t-esc="row.cols.rem" />
|
||||
</t>
|
||||
</td>
|
||||
<td class="brctbl-col3 text-center">
|
||||
<t t-esc="row.cols.qty"/> <t t-esc="row.cols.uom" />
|
||||
</td>
|
||||
<td class="brctbl-col4 js_loc">
|
||||
<t t-esc="row.cols.loc" />
|
||||
<t t-if="row.cols.pack" ><span> : <t t-esc="row.cols.pack" /></span></t>
|
||||
<t t-if="row.cols.lot" ><span> : <t t-esc="row.cols.lot" /></span></t>
|
||||
</td>
|
||||
<td class="brctbl-col5 js_loc"> <t t-esc="row.cols.dest" />
|
||||
<div class="pull-right btn-group">
|
||||
<button type="button" class="btn btn-default dropdown-toggle fa fa-cog" data-toggle="dropdown">
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
<t t-if="row.cols.product_id">
|
||||
<li><a class="js_create_lot" href="#">Create / Change Lot</a></li>
|
||||
</t>
|
||||
<t t-if="!row.cols.head_container && !row.cols.container">
|
||||
<li><a class="js_change_src" href="#">Change source location</a></li>
|
||||
<li><a class="js_change_dst" href="#">Change destination location</a></li>
|
||||
</t>
|
||||
<t t-if="row.cols.head_container">
|
||||
<li><a class="js_pack_change_dst" href="#">Change destination location</a></li>
|
||||
<li class="divider"></li>
|
||||
<li><a class="js_pack_configure" href="#">Configure package</a></li>
|
||||
<li><a class="js_delete_pack" href="#">Remove from package</a></li>
|
||||
<li><a class="js_print_pack" href="#">Print package label</a></li>
|
||||
</t>
|
||||
</ul>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</t>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<t t-name="PickingSearchResults">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">Search Results</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<t t-if="results.length === 0">
|
||||
<strong>No picking found.</strong>
|
||||
</t>
|
||||
<t t-if="results.length > 0">
|
||||
<t t-foreach="results" t-as="picking">
|
||||
<div class="col-lg-3 col-md-4">
|
||||
<div class="panel panel-default oe_picking" t-att-data-id="picking.id">
|
||||
<div class="panel-body">
|
||||
<strong class='oe_picking_name'><t t-esc="picking.name" /></strong>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<t t-name="PickingMenuWidget">
|
||||
<div class="navbar navbar-inverse navbar-static-top" role="navigation">
|
||||
<div class="container">
|
||||
<div class="navbar-header navbar-form navbar-left">
|
||||
<input type='text' class="oe_searchbox form-control pull-left" placeholder='Search'/>
|
||||
</div>
|
||||
<div class="navbar-header navbar-form navbar-right">
|
||||
<button type="button" class="btn btn-danger js_pick_quit pull-right">Quit</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="container">
|
||||
|
||||
<h1 class="js_title_label">Select your operation</h1>
|
||||
<div class='js_picking_not_found alert alert-warning hidden'>
|
||||
Scanned picking could not be found
|
||||
</div>
|
||||
|
||||
<div class='js_picking_search_results panel panel-info hidden'>
|
||||
</div>
|
||||
|
||||
<div class="row js_picking_categories">
|
||||
<t t-foreach="widget.picking_types" t-as="type">
|
||||
<div class="col-lg-3 col-md-4">
|
||||
<div t-att-class="'oe_kanban oe_picking oe_kanban_color_' + type.color + ' ' + (widget.pickings_by_type[type.id].length === 0 ? 'oe_empty':'js_pick_last') "
|
||||
t-att-data-id="type.id">
|
||||
<t t-if="type.code == 'incoming'" ><span class="fa fa-sign-in fa-2x"></span></t>
|
||||
<t t-if="type.code == 'outgoing'" ><span class="fa fa-truck fa-2x fa-flip-horizontal"></span></t>
|
||||
<t t-if="type.code == 'internal'" ><span class="fa fa-retweet fa-2x"></span></t>
|
||||
<strong><span><t t-esc="type.complete_name"/></span></strong>
|
||||
<div><t t-if="widget.pickings_by_type[type.id].length > 0">
|
||||
<span class='badge'><t t-esc="widget.pickings_by_type[type.id].length" /> picking(s) </span>
|
||||
</t></div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
|
||||
<t t-name="PickingMainWidget">
|
||||
<div class="navbar navbar-inverse navbar-static-top" role="navigation">
|
||||
<div class="container">
|
||||
<div class="navbar-left">
|
||||
<button type="button" class="btn btn-primary navbar-btn js_pick_menu">Menu</button>
|
||||
</div>
|
||||
<div class="navbar-right">
|
||||
<button type="button" class="btn btn-default navbar-btn js_pick_prev">< Previous</button>
|
||||
<button type="button" class="btn btn-default navbar-btn js_pick_next">Next ></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<div class='oe_placeholder_picking_editor'/>
|
||||
<div class="text-right small oe_reload_op">
|
||||
The reserved stock changed. You might want to <button class="btn btn-default js_reload_op"> Recompute </button> the operations.
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
+242
-213
@@ -12,7 +12,7 @@ from openerp.osv import fields, osv
|
||||
from openerp.tools.float_utils import float_compare, float_round
|
||||
from openerp.tools.translate import _
|
||||
from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT, DEFAULT_SERVER_DATE_FORMAT
|
||||
from openerp import SUPERUSER_ID, api, models, fields as new_fields
|
||||
from openerp import SUPERUSER_ID, api, models
|
||||
import openerp.addons.decimal_precision as dp
|
||||
from openerp.addons.procurement import procurement
|
||||
import logging
|
||||
@@ -365,9 +365,6 @@ class stock_quant(osv.osv):
|
||||
#reserve quants
|
||||
if toreserve:
|
||||
self.write(cr, SUPERUSER_ID, toreserve, {'reservation_id': move.id}, context=context)
|
||||
#if move has a picking_id, write on that picking that pack_operation might have changed and need to be recomputed
|
||||
if move.picking_id:
|
||||
self.pool.get('stock.picking').write(cr, uid, [move.picking_id.id], {'recompute_pack_op': True}, context=context)
|
||||
#check if move'state needs to be set as 'assigned'
|
||||
rounding = move.product_id.uom_id.rounding
|
||||
if float_compare(reserved_availability, move.product_qty, precision_rounding=rounding) == 0 and move.state in ('confirmed', 'waiting') :
|
||||
@@ -597,8 +594,6 @@ class stock_quant(osv.osv):
|
||||
related_quants = [x.id for x in move.reserved_quant_ids]
|
||||
if related_quants:
|
||||
#if move has a picking_id, write on that picking that pack_operation might have changed and need to be recomputed
|
||||
if move.picking_id:
|
||||
self.pool.get('stock.picking').write(cr, uid, [move.picking_id.id], {'recompute_pack_op': True}, context=context)
|
||||
if move.partially_available:
|
||||
self.pool.get("stock.move").write(cr, uid, [move.id], {'partially_available': False}, context=context)
|
||||
self.write(cr, SUPERUSER_ID, related_quants, {'reservation_id': False}, context=context)
|
||||
@@ -643,7 +638,7 @@ class stock_quant(osv.osv):
|
||||
# Stock Picking
|
||||
#----------------------------------------------------------
|
||||
|
||||
class stock_picking(osv.osv):
|
||||
class stock_picking(models.Model):
|
||||
_name = "stock.picking"
|
||||
_inherit = ['mail.thread']
|
||||
_description = "Transfer"
|
||||
@@ -693,8 +688,31 @@ class stock_picking(osv.osv):
|
||||
ptype_id = vals.get('picking_type_id', context.get('default_picking_type_id', False))
|
||||
sequence_id = self.pool.get('stock.picking.type').browse(cr, user, ptype_id, context=context).sequence_id.id
|
||||
vals['name'] = self.pool.get('ir.sequence').next_by_id(cr, user, sequence_id, context=context)
|
||||
# As the on_change in one2many list is WIP, we will overwrite the locations on the stock moves here
|
||||
# As it is a create the format will be a list of (0, 0, dict)
|
||||
if vals.get('move_lines') and vals.get('location_id') and vals.get('location_dest_id'):
|
||||
for move in vals['move_lines']:
|
||||
if len(move) == 3:
|
||||
move[2]['location_id'] = vals['location_id']
|
||||
move[2]['location_dest_id'] = vals['location_dest_id']
|
||||
return super(stock_picking, self).create(cr, user, vals, context)
|
||||
|
||||
def write(self, cr, uid, ids, vals, context=None):
|
||||
res = super(stock_picking, self).write(cr, uid, ids, vals, context=context)
|
||||
after_vals = {}
|
||||
if vals.get('location_id'):
|
||||
after_vals['location_id'] = vals['location_id']
|
||||
if vals.get('location_dest_id'):
|
||||
after_vals['location_dest_id'] = vals['location_dest_id']
|
||||
# Change locations of moves if those of the picking change
|
||||
if after_vals:
|
||||
moves = []
|
||||
for pick in self.browse(cr, uid, ids, context=context):
|
||||
moves += [x.id for x in pick.move_lines if not x.scrapped]
|
||||
if moves:
|
||||
self.pool['stock.move'].write(cr, uid, moves, after_vals, context=context)
|
||||
return res
|
||||
|
||||
def _state_get(self, cr, uid, ids, field_name, arg, context=None):
|
||||
'''The state of a picking depends on the state of its related stock.move
|
||||
draft: the picking has no line or any one of the lines is draft
|
||||
@@ -703,7 +721,10 @@ class stock_picking(osv.osv):
|
||||
'''
|
||||
res = {}
|
||||
for pick in self.browse(cr, uid, ids, context=context):
|
||||
if (not pick.move_lines) or any([x.state == 'draft' for x in pick.move_lines]):
|
||||
if not pick.move_lines:
|
||||
res[pick.id] = pick.launch_pack_operations and 'assigned' or 'draft'
|
||||
continue
|
||||
if any([x.state == 'draft' for x in pick.move_lines]):
|
||||
res[pick.id] = 'draft'
|
||||
continue
|
||||
if all([x.state == 'cancel' for x in pick.move_lines]):
|
||||
@@ -759,19 +780,33 @@ class stock_picking(osv.osv):
|
||||
continue
|
||||
return res
|
||||
|
||||
def check_group_lot(self, cr, uid, context=None):
|
||||
""" This function will return true if we have the setting to use lots activated. """
|
||||
return self.pool.get('res.users').has_group(cr, uid, 'stock.group_production_lot')
|
||||
|
||||
def check_group_pack(self, cr, uid, context=None):
|
||||
""" This function will return true if we have the setting to use package activated. """
|
||||
return self.pool.get('res.users').has_group(cr, uid, 'stock.group_tracking_lot')
|
||||
|
||||
def action_assign_owner(self, cr, uid, ids, context=None):
|
||||
for picking in self.browse(cr, uid, ids, context=context):
|
||||
packop_ids = [op.id for op in picking.pack_operation_ids]
|
||||
self.pool.get('stock.pack.operation').write(cr, uid, packop_ids, {'owner_id': picking.owner_id.id}, context=context)
|
||||
|
||||
def onchange_picking_type(self, cr, uid, ids, picking_type_id):
|
||||
res = {}
|
||||
if picking_type_id:
|
||||
picking_type = self.pool['stock.picking.type'].browse(cr, uid, picking_type_id)
|
||||
res['value'] = {'location_id': picking_type.default_location_src_id.id,
|
||||
'location_dest_id': picking_type.default_location_dest_id.id}
|
||||
return res
|
||||
|
||||
def _default_location_destination(self):
|
||||
context = self._context or {}
|
||||
if context.get('default_picking_type_id', False):
|
||||
pick_type = self.env['stock.picking.type'].browse(context['default_picking_type_id'])
|
||||
return pick_type.default_location_dest_id and pick_type.default_location_dest_id.id or False
|
||||
return False
|
||||
|
||||
def _default_location_source(self):
|
||||
context = self._context or {}
|
||||
if context.get('default_picking_type_id', False):
|
||||
pick_type = self.env['stock.picking.type'].browse(context['default_picking_type_id'])
|
||||
return pick_type.default_location_src_id and pick_type.default_location_src_id.id or False
|
||||
return False
|
||||
|
||||
_columns = {
|
||||
'name': fields.char('Reference', select=True, states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}, copy=False),
|
||||
'origin': fields.char('Source Document', states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}, help="Reference of the document", select=True),
|
||||
@@ -780,7 +815,7 @@ class stock_picking(osv.osv):
|
||||
'move_type': fields.selection([('direct', 'Partial'), ('one', 'All at once')], 'Delivery Method', required=True, states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}, help="It specifies goods to be deliver partially or all at once"),
|
||||
'state': fields.function(_state_get, type="selection", copy=False,
|
||||
store={
|
||||
'stock.picking': (lambda self, cr, uid, ids, ctx: ids, ['move_type'], 20),
|
||||
'stock.picking': (lambda self, cr, uid, ids, ctx: ids, ['move_type', 'launch_pack_operations'], 20),
|
||||
'stock.move': (_get_pickings, ['state', 'picking_id', 'partially_available'], 20)},
|
||||
selection=[
|
||||
('draft', 'Draft'),
|
||||
@@ -800,6 +835,15 @@ class stock_picking(osv.osv):
|
||||
* Transferred: has been processed, can't be modified or cancelled anymore\n
|
||||
* Cancelled: has been cancelled, can't be confirmed anymore"""
|
||||
),
|
||||
'location_id': fields.many2one('stock.location', required=True, string="Source Location Zone",
|
||||
default=_default_location_source, readonly=True, states={'draft': [('readonly', False)]}),
|
||||
'location_dest_id': fields.many2one('stock.location', required=True,string="Destination Location Zone",
|
||||
default=_default_location_destination, readonly=True, states={'draft': [('readonly', False)]}),
|
||||
'move_lines': fields.one2many('stock.move', 'picking_id', string="Stock Moves", copy=True),
|
||||
'move_lines_related': fields.related('move_lines', type='one2many', relation='stock.move', string="Move Lines"),
|
||||
'picking_type_id': fields.many2one('stock.picking.type', 'Picking Type', states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}, required=True),
|
||||
'picking_type_code': fields.related('picking_type_id', 'code', type='selection', selection=[('incoming', 'Suppliers'), ('outgoing', 'Customers'), ('internal', 'Internal')]),
|
||||
'picking_type_entire_packs': fields.related('picking_type_id', 'show_entire_packs', type='boolean'),
|
||||
'priority': fields.function(get_min_max_date, multi="min_max_date", fnct_inv=_set_priority, type='selection', selection=procurement.PROCUREMENT_PRIORITIES, string='Priority',
|
||||
store={'stock.move': (_get_pickings, ['priority', 'picking_id'], 20)}, states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}, select=1, help="Priority for this picking. Setting manually a value here would set it as priority for all the moves",
|
||||
track_visibility='onchange', required=True),
|
||||
@@ -809,30 +853,23 @@ class stock_picking(osv.osv):
|
||||
store={'stock.move': (_get_pickings, ['date_expected', 'picking_id'], 20)}, type='datetime', string='Max. Expected Date', select=2, help="Scheduled time for the last part of the shipment to be processed"),
|
||||
'date': fields.datetime('Creation Date', help="Creation Date, usually the time of the order", select=True, states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}, track_visibility='onchange'),
|
||||
'date_done': fields.datetime('Date of Transfer', help="Completion Date of Transfer", readonly=True, copy=False),
|
||||
'move_lines': fields.one2many('stock.move', 'picking_id', 'Internal Moves', states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}, copy=True),
|
||||
'quant_reserved_exist': fields.function(_get_quant_reserved_exist, type='boolean', string='Quant already reserved ?', help='technical field used to know if there is already at least one quant reserved on moves of a given picking'),
|
||||
'partner_id': fields.many2one('res.partner', 'Partner', states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}),
|
||||
'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', states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}, string='Related Packing Operations'),
|
||||
'pack_operation_product_ids': fields.one2many('stock.pack.operation', 'picking_id', states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}, domain=[('product_id', '!=', False)], string='Non pack'),
|
||||
'pack_operation_pack_ids': fields.one2many('stock.pack.operation', 'picking_id', states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}, domain=[('product_id', '=', False)], string='Pack'),
|
||||
'pack_operation_exist': fields.function(_get_pack_operation_exist, type='boolean', string='Pack Operation Exists?', help='technical field for attrs in view'),
|
||||
'picking_type_id': fields.many2one('stock.picking.type', 'Picking Type', states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}, required=True),
|
||||
'picking_type_code': fields.related('picking_type_id', 'code', type='char', string='Picking Type Code', help="Technical field used to display the correct label on print button in the picking view"),
|
||||
|
||||
'owner_id': fields.many2one('res.partner', 'Owner', states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}, help="Default Owner"),
|
||||
# Used to search on pickings
|
||||
'product_id': fields.related('move_lines', 'product_id', type='many2one', relation='product.product', string='Product'),
|
||||
'recompute_pack_op': fields.boolean('Recompute pack operation?', help='True if reserved quants changed, which mean we might need to recompute the package operations', copy=False),
|
||||
'location_id': fields.related('move_lines', 'location_id', type='many2one', relation='stock.location', string='Location',
|
||||
readonly=True, store={'stock.picking': (lambda self, cr, uid, ids, ctx: ids, ['move_lines'], 10),
|
||||
'stock.move': (_get_pickings, ['location_id', 'picking_id'], 10),}),
|
||||
'location_dest_id': fields.related('move_lines', 'location_dest_id', type='many2one', relation='stock.location', string='Destination Location',
|
||||
readonly=True, store={'stock.picking': (lambda self, cr, uid, ids, ctx: ids, ['move_lines'], 10),
|
||||
'stock.move': (_get_pickings, ['location_dest_id', 'picking_id'], 10),}),
|
||||
'group_id': fields.related('move_lines', 'group_id', type='many2one', relation='procurement.group', string='Procurement Group', readonly=True,
|
||||
store={
|
||||
'stock.picking': (lambda self, cr, uid, ids, ctx: ids, ['move_lines'], 10),
|
||||
'stock.move': (_get_pickings, ['group_id', 'picking_id'], 10),
|
||||
}),
|
||||
'launch_pack_operations': fields.boolean("Launch Pack Operations", copy=False),
|
||||
}
|
||||
|
||||
_defaults = {
|
||||
@@ -842,7 +879,8 @@ class stock_picking(osv.osv):
|
||||
'priority': '1', # normal
|
||||
'date': fields.datetime.now,
|
||||
'company_id': lambda self, cr, uid, c: self.pool.get('res.company')._company_default_get(cr, uid, 'stock.picking', context=c),
|
||||
'recompute_pack_op': True,
|
||||
'recompute_pack_op': False,
|
||||
'launch_pack_operations': False,
|
||||
}
|
||||
_sql_constraints = [
|
||||
('name_uniq', 'unique(name, company_id)', 'Reference must be unique per company!'),
|
||||
@@ -858,10 +896,15 @@ class stock_picking(osv.osv):
|
||||
context = dict(context or {}, active_ids=ids)
|
||||
return self.pool.get("report").get_action(cr, uid, ids, 'stock.report_picking_operations', context=context)
|
||||
|
||||
def launch_packops(self, cr, uid, ids, context=None):
|
||||
self.write(cr, uid, ids, {'launch_pack_operations': True}, context=context)
|
||||
|
||||
def action_confirm(self, cr, uid, ids, context=None):
|
||||
todo = []
|
||||
todo_force_assign = []
|
||||
for picking in self.browse(cr, uid, ids, context=context):
|
||||
if not picking.move_lines:
|
||||
self.launch_packops(cr, uid, [picking.id], context=context)
|
||||
if picking.location_id.usage in ('supplier', 'inventory', 'production'):
|
||||
todo_force_assign.append(picking.id)
|
||||
for r in picking.move_lines:
|
||||
@@ -894,11 +937,10 @@ class stock_picking(osv.osv):
|
||||
""" Changes state of picking to available if moves are confirmed or waiting.
|
||||
@return: True
|
||||
"""
|
||||
for pick in self.browse(cr, uid, ids, context=context):
|
||||
pickings = self.browse(cr, uid, ids, context=context)
|
||||
for pick in pickings:
|
||||
move_ids = [x.id for x in pick.move_lines if x.state in ['confirmed', 'waiting']]
|
||||
self.pool.get('stock.move').force_assign(cr, uid, move_ids, context=context)
|
||||
#pack_operation might have changed and need to be recomputed
|
||||
self.write(cr, uid, ids, {'recompute_pack_op': True}, context=context)
|
||||
return True
|
||||
|
||||
def action_cancel(self, cr, uid, ids, context=None):
|
||||
@@ -934,19 +976,6 @@ class stock_picking(osv.osv):
|
||||
move_obj.unlink(cr, uid, move_ids, context=context)
|
||||
return super(stock_picking, self).unlink(cr, uid, ids, context=context)
|
||||
|
||||
def write(self, cr, uid, ids, vals, context=None):
|
||||
if vals.get('move_lines') and not vals.get('pack_operation_ids'):
|
||||
# pack operations are directly dependant of move lines, it needs to be recomputed
|
||||
pack_operation_obj = self.pool['stock.pack.operation']
|
||||
existing_package_ids = pack_operation_obj.search(cr, uid, [('picking_id', 'in', ids)], context=context)
|
||||
if existing_package_ids:
|
||||
pack_operation_obj.unlink(cr, uid, existing_package_ids, context)
|
||||
res = super(stock_picking, self).write(cr, uid, ids, vals, context=context)
|
||||
#if we changed the move lines or the pack operations, we need to recompute the remaining quantities of both
|
||||
if 'move_lines' in vals or 'pack_operation_ids' in vals:
|
||||
self.do_recompute_remaining_quantities(cr, uid, ids, context=context)
|
||||
return res
|
||||
|
||||
def _create_backorder(self, cr, uid, picking, backorder_moves=[], context=None):
|
||||
""" Move all non-done lines into a new backorder picking. If the key 'do_only_split' is given in the context, then move all lines not in context.get('split', []) instead of all non-done lines.
|
||||
"""
|
||||
@@ -970,6 +999,7 @@ class stock_picking(osv.osv):
|
||||
|
||||
self.write(cr, uid, [picking.id], {'date_done': time.strftime(DEFAULT_SERVER_DATETIME_FORMAT)}, context=context)
|
||||
self.action_confirm(cr, uid, [backorder_id], context=context)
|
||||
self.action_assign(cr, uid, [backorder_id], context=context)
|
||||
return backorder_id
|
||||
return False
|
||||
|
||||
@@ -1130,16 +1160,6 @@ class stock_picking(osv.osv):
|
||||
processed_products.add(move.product_id.id)
|
||||
return vals
|
||||
|
||||
@api.cr_uid_ids_context
|
||||
def open_barcode_interface(self, cr, uid, picking_ids, context=None):
|
||||
final_url="/stock/barcode/#action=stock.ui&picking_id="+str(picking_ids[0])
|
||||
return {'type': 'ir.actions.act_url', 'url':final_url, 'target': 'self',}
|
||||
|
||||
@api.cr_uid_ids_context
|
||||
def do_partial_open_barcode(self, cr, uid, picking_ids, context=None):
|
||||
self.do_prepare_partial(cr, uid, picking_ids, context=context)
|
||||
return self.open_barcode_interface(cr, uid, picking_ids, context=context)
|
||||
|
||||
@api.cr_uid_ids_context
|
||||
def do_prepare_partial(self, cr, uid, picking_ids, context=None):
|
||||
context = context or {}
|
||||
@@ -1169,6 +1189,7 @@ class stock_picking(osv.osv):
|
||||
else:
|
||||
forced_qties[move.product_id] = forced_qty
|
||||
for vals in self._prepare_pack_ops(cr, uid, picking, picking_quants, forced_qties, context=context):
|
||||
vals['fresh_record'] = False
|
||||
pack_operation_obj.create(cr, uid, vals, context=ctx)
|
||||
#recompute the remaining quantities all at once
|
||||
self.do_recompute_remaining_quantities(cr, uid, picking_ids, context=context)
|
||||
@@ -1189,7 +1210,7 @@ class stock_picking(osv.osv):
|
||||
self.pool.get('stock.pack.operation').unlink(cr, uid, pack_line_to_unreserve, context=context)
|
||||
self.pool.get('stock.move').do_unreserve(cr, uid, moves_to_unreserve, context=context)
|
||||
|
||||
def recompute_remaining_qty(self, cr, uid, picking, context=None):
|
||||
def recompute_remaining_qty(self, cr, uid, picking, done_qtys=False, context=None):
|
||||
def _create_link_for_index(operation_id, index, product_id, qty_to_assign, quant_id=False):
|
||||
move_dict = prod2move_ids[product_id][index]
|
||||
qty_on_link = min(move_dict['remaining_qty'], qty_to_assign)
|
||||
@@ -1253,7 +1274,7 @@ class stock_picking(osv.osv):
|
||||
for ops in operations:
|
||||
#for each operation, create the links with the stock move by seeking on the matching reserved quants,
|
||||
#and deffer the operation if there is some ambiguity on the move to select
|
||||
if ops.package_id and not ops.product_id:
|
||||
if ops.package_id and not ops.product_id and (not done_qtys or ops.product_qty):
|
||||
#entire package
|
||||
quant_ids = package_obj.get_content(cr, uid, [ops.package_id.id], context=context)
|
||||
for quant in quant_obj.browse(cr, uid, quant_ids, context=context):
|
||||
@@ -1268,7 +1289,8 @@ class stock_picking(osv.osv):
|
||||
need_rereserve = True
|
||||
elif ops.product_id.id:
|
||||
#Check moves with same product
|
||||
qty_to_assign = uom_obj._compute_qty_obj(cr, uid, ops.product_uom_id, ops.product_qty, ops.product_id.uom_id, context=context)
|
||||
product_qty = done_qtys and ops.qty_done or ops.product_qty
|
||||
qty_to_assign = uom_obj._compute_qty_obj(cr, uid, ops.product_uom_id, product_qty, ops.product_id.uom_id, context=context)
|
||||
for move_dict in prod2move_ids.get(ops.product_id.id, []):
|
||||
move = move_dict['move']
|
||||
for quant in move.reserved_quant_ids:
|
||||
@@ -1301,18 +1323,18 @@ class stock_picking(osv.osv):
|
||||
all_op_processed = _create_link_for_product(ops.id, product_id, remaining_qty) and all_op_processed
|
||||
return (need_rereserve, all_op_processed)
|
||||
|
||||
def picking_recompute_remaining_quantities(self, cr, uid, picking, context=None):
|
||||
def picking_recompute_remaining_quantities(self, cr, uid, picking, done_qtys=False, context=None):
|
||||
need_rereserve = False
|
||||
all_op_processed = True
|
||||
if picking.pack_operation_ids:
|
||||
need_rereserve, all_op_processed = self.recompute_remaining_qty(cr, uid, picking, context=context)
|
||||
need_rereserve, all_op_processed = self.recompute_remaining_qty(cr, uid, picking, done_qtys=done_qtys, context=context)
|
||||
return need_rereserve, all_op_processed
|
||||
|
||||
@api.cr_uid_ids_context
|
||||
def do_recompute_remaining_quantities(self, cr, uid, picking_ids, context=None):
|
||||
def do_recompute_remaining_quantities(self, cr, uid, picking_ids, done_qtys=False, context=None):
|
||||
for picking in self.browse(cr, uid, picking_ids, context=context):
|
||||
if picking.pack_operation_ids:
|
||||
self.recompute_remaining_qty(cr, uid, picking, context=context)
|
||||
self.recompute_remaining_qty(cr, uid, picking, done_qtys=done_qtys, context=context)
|
||||
|
||||
def _prepare_values_extra_move(self, cr, uid, op, product, remaining_qty, context=None):
|
||||
"""
|
||||
@@ -1371,33 +1393,75 @@ class stock_picking(osv.osv):
|
||||
self.action_assign(cr, uid, [picking.id], context=context)
|
||||
else:
|
||||
stock_move_obj.do_unreserve(cr, uid, move_ids, context=context)
|
||||
stock_move_obj.action_assign(cr, uid, move_ids, context=context)
|
||||
stock_move_obj.action_assign(cr, uid, move_ids, no_prepare=True, context=context)
|
||||
|
||||
@api.cr_uid_ids_context
|
||||
def do_enter_transfer_details(self, cr, uid, picking, context=None):
|
||||
if not context:
|
||||
context = {}
|
||||
def do_new_transfer(self, cr, uid, ids, context=None):
|
||||
pack_op_obj = self.pool['stock.pack.operation']
|
||||
data_obj = self.pool['ir.model.data']
|
||||
for pick in self.browse(cr, uid, ids, context=context):
|
||||
to_delete = []
|
||||
# In draft or with no pack operations edited yet, ask if we can just do everything
|
||||
if pick.state == 'draft' or all([x.qty_done == 0.0 for x in pick.pack_operation_ids]):
|
||||
view = data_obj.xmlid_to_res_id(cr, uid, 'stock.view_immediate_transfer')
|
||||
wiz_id = self.pool['stock.immediate.transfer'].create(cr, uid, {'pick_id': pick.id}, context=context)
|
||||
return {
|
||||
'name': _('Immediate Transfer?'),
|
||||
'type': 'ir.actions.act_window',
|
||||
'view_type': 'form',
|
||||
'view_mode': 'form',
|
||||
'res_model': 'stock.immediate.transfer',
|
||||
'views': [(view, 'form')],
|
||||
'view_id': view,
|
||||
'target': 'new',
|
||||
'res_id': wiz_id,
|
||||
'context': context,
|
||||
}
|
||||
|
||||
context.update({
|
||||
'active_model': self._name,
|
||||
'active_ids': picking,
|
||||
'active_id': len(picking) and picking[0] or False
|
||||
})
|
||||
# Check backorder should check for other barcodes
|
||||
if self.check_backorder(cr, uid, pick, context=context):
|
||||
view = data_obj.xmlid_to_res_id(cr, uid, 'stock.view_backorder_confirmation')
|
||||
wiz_id = self.pool['stock.backorder.confirmation'].create(cr, uid, {'pick_id': pick.id}, context=context)
|
||||
return {
|
||||
'name': _('Create Backorder?'),
|
||||
'type': 'ir.actions.act_window',
|
||||
'view_type': 'form',
|
||||
'view_mode': 'form',
|
||||
'res_model': 'stock.backorder.confirmation',
|
||||
'views': [(view, 'form')],
|
||||
'view_id': view,
|
||||
'target': 'new',
|
||||
'res_id': wiz_id,
|
||||
'context': context,
|
||||
}
|
||||
for operation in pick.pack_operation_ids:
|
||||
if operation.qty_done < 0:
|
||||
raise UserError(_('No negative quantities allowed'))
|
||||
if operation.qty_done > 0:
|
||||
pack_op_obj.write(cr, uid, operation.id, {'product_qty': operation.qty_done}, context=context)
|
||||
else:
|
||||
to_delete.append(operation.id)
|
||||
if to_delete:
|
||||
pack_op_obj.unlink(cr, uid, to_delete, context=context)
|
||||
self.do_transfer(cr, uid, ids, context=context)
|
||||
return
|
||||
|
||||
created_id = self.pool['stock.transfer_details'].create(cr, uid, {'picking_id': len(picking) and picking[0] or False}, context)
|
||||
return self.pool['stock.transfer_details'].wizard_view(cr, uid, created_id, context)
|
||||
def check_backorder(self, cr, uid, picking, context=None):
|
||||
need_rereserve, all_op_processed = self.picking_recompute_remaining_quantities(cr, uid, picking, done_qtys=True, context=context)
|
||||
for move in picking.move_lines:
|
||||
if float_compare(move.remaining_qty, 0, precision_rounding = move.product_id.uom_id.rounding) != 0:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
@api.cr_uid_ids_context
|
||||
def do_transfer(self, cr, uid, picking_ids, context=None):
|
||||
def do_transfer(self, cr, uid, ids, context=None):
|
||||
"""
|
||||
If no pack operation, we do simple action_done of the picking
|
||||
Otherwise, do the pack operations
|
||||
"""
|
||||
if not context:
|
||||
context = {}
|
||||
|
||||
stock_move_obj = self.pool.get('stock.move')
|
||||
for picking in self.browse(cr, uid, picking_ids, context=context):
|
||||
for picking in self.browse(cr, uid, ids, context=context):
|
||||
if not picking.pack_operation_ids:
|
||||
self.action_done(cr, uid, [picking.id], context=context)
|
||||
continue
|
||||
@@ -1435,8 +1499,6 @@ class stock_picking(osv.osv):
|
||||
elif context.get('do_only_split'):
|
||||
context = dict(context, split=todo_move_ids)
|
||||
self._create_backorder(cr, uid, picking, context=context)
|
||||
if toassign_move_ids:
|
||||
stock_move_obj.action_assign(cr, uid, toassign_move_ids, context=context)
|
||||
return True
|
||||
|
||||
@api.cr_uid_ids_context
|
||||
@@ -1448,115 +1510,26 @@ class stock_picking(osv.osv):
|
||||
ctx['do_only_split'] = True
|
||||
return self.do_transfer(cr, uid, picking_ids, context=ctx)
|
||||
|
||||
def get_next_picking_for_ui(self, cr, uid, context=None):
|
||||
""" returns the next pickings to process. Used in the barcode scanner UI"""
|
||||
if context is None:
|
||||
context = {}
|
||||
domain = [('state', 'in', ('assigned', 'partially_available'))]
|
||||
if context.get('default_picking_type_id'):
|
||||
domain.append(('picking_type_id', '=', context['default_picking_type_id']))
|
||||
return self.search(cr, uid, domain, context=context)
|
||||
|
||||
def action_done_from_ui(self, cr, uid, picking_id, context=None):
|
||||
""" called when button 'done' is pushed in the barcode scanner UI """
|
||||
#write qty_done into field product_qty for every package_operation before doing the transfer
|
||||
pack_op_obj = self.pool.get('stock.pack.operation')
|
||||
for operation in self.browse(cr, uid, picking_id, context=context).pack_operation_ids:
|
||||
pack_op_obj.write(cr, uid, operation.id, {'product_qty': operation.qty_done}, context=context)
|
||||
self.do_transfer(cr, uid, [picking_id], context=context)
|
||||
#return id of next picking to work on
|
||||
return self.get_next_picking_for_ui(cr, uid, context=context)
|
||||
|
||||
@api.cr_uid_ids_context
|
||||
def action_pack(self, cr, uid, picking_ids, operation_filter_ids=None, context=None):
|
||||
""" Create a package with the current pack_operation_ids of the picking that aren't yet in a pack.
|
||||
Used in the barcode scanner UI and the normal interface as well.
|
||||
operation_filter_ids is used by barcode scanner interface to specify a subset of operation to pack"""
|
||||
if operation_filter_ids == None:
|
||||
operation_filter_ids = []
|
||||
stock_operation_obj = self.pool.get('stock.pack.operation')
|
||||
package_obj = self.pool.get('stock.quant.package')
|
||||
stock_move_obj = self.pool.get('stock.move')
|
||||
package_id = False
|
||||
for picking_id in picking_ids:
|
||||
operation_search_domain = [('picking_id', '=', picking_id), ('result_package_id', '=', False)]
|
||||
if operation_filter_ids != []:
|
||||
operation_search_domain.append(('id', 'in', operation_filter_ids))
|
||||
operation_ids = stock_operation_obj.search(cr, uid, operation_search_domain, context=context)
|
||||
def put_in_pack(self, cr, uid, ids, context=None):
|
||||
stock_move_obj = self.pool["stock.move"]
|
||||
stock_operation_obj = self.pool["stock.pack.operation"]
|
||||
package_obj = self.pool["stock.quant.package"]
|
||||
for pick in self.browse(cr, uid, ids, context=context):
|
||||
operations = [x for x in pick.pack_operation_ids if x.qty_done > 0 and (not x.result_package_id)]
|
||||
pack_operation_ids = []
|
||||
if operation_ids:
|
||||
for operation in stock_operation_obj.browse(cr, uid, operation_ids, context=context):
|
||||
#If we haven't done all qty in operation, we have to split into 2 operation
|
||||
op = operation
|
||||
if (operation.qty_done < operation.product_qty):
|
||||
new_operation = stock_operation_obj.copy(cr, uid, operation.id, {'product_qty': operation.qty_done,'qty_done': operation.qty_done}, context=context)
|
||||
stock_operation_obj.write(cr, uid, operation.id, {'product_qty': operation.product_qty - operation.qty_done,'qty_done': 0, 'lot_id': False}, context=context)
|
||||
op = stock_operation_obj.browse(cr, uid, new_operation, context=context)
|
||||
pack_operation_ids.append(op.id)
|
||||
if op.product_id and op.location_id and op.location_dest_id:
|
||||
stock_move_obj.check_tracking_product(cr, uid, op.product_id, op.lot_id.id, op.location_id, op.location_dest_id, context=context)
|
||||
for operation in operations:
|
||||
#If we haven't done all qty in operation, we have to split into 2 operation
|
||||
op = operation
|
||||
if operation.qty_done < operation.product_qty:
|
||||
new_operation = stock_operation_obj.copy(cr, uid, operation.id, {'product_qty': operation.qty_done,'qty_done': operation.qty_done}, context=context)
|
||||
stock_operation_obj.write(cr, uid, operation.id, {'product_qty': operation.product_qty - operation.qty_done,'qty_done': 0, 'lot_id': False}, context=context)
|
||||
op = stock_operation_obj.browse(cr, uid, new_operation, context=context)
|
||||
pack_operation_ids.append(op.id)
|
||||
if op.product_id and op.location_id and op.location_dest_id:
|
||||
stock_move_obj.check_tracking_product(cr, uid, op.product_id, op.lot_id.id, op.location_id, op.location_dest_id, context=context)
|
||||
if operations:
|
||||
package_id = package_obj.create(cr, uid, {}, context=context)
|
||||
stock_operation_obj.write(cr, uid, pack_operation_ids, {'result_package_id': package_id}, context=context)
|
||||
return package_id
|
||||
|
||||
def process_product_id_from_ui(self, cr, uid, picking_id, product_id, op_id, increment=1, context=None):
|
||||
return self.pool.get('stock.pack.operation')._search_and_increment(cr, uid, picking_id, [('product_id', '=', product_id),('id', '=', op_id)], increment=increment, context=context)
|
||||
|
||||
def process_barcode_from_ui(self, cr, uid, picking_id, barcode_str, visible_op_ids, context=None):
|
||||
'''This function is called each time there barcode scanner reads an input'''
|
||||
stock_operation_obj = self.pool.get('stock.pack.operation')
|
||||
answer = {'filter_loc': False, 'operation_id': False}
|
||||
|
||||
# Barcode Nomenclatures
|
||||
picking_type_id = self.browse(cr, uid, [picking_id], context=context).picking_type_id.id
|
||||
barcode_nom = self.pool.get('stock.picking.type').browse(cr, uid, [picking_type_id], context=context).barcode_nomenclature_id
|
||||
parsed_result = barcode_nom.parse_barcode(barcode_str)
|
||||
|
||||
#check if the barcode is a weighted barcode or simply a product
|
||||
if parsed_result['type'] in ['weight', 'product', 'package']:
|
||||
weight=1
|
||||
if parsed_result['type'] == 'weight':
|
||||
domain = ['|', ('barcode', '=', parsed_result['base_code']), ('default_code', '=', parsed_result['base_code'])]
|
||||
weight=parsed_result['value']
|
||||
obj = self.pool.get('product.product')
|
||||
id_in_operation = 'product_id'
|
||||
elif parsed_result['type'] == 'product':
|
||||
domain = ['|', ('barcode', '=', parsed_result['code']), ('default_code', '=', parsed_result['code'])]
|
||||
obj = self.pool.get('product.product')
|
||||
id_in_operation = 'product_id'
|
||||
else:
|
||||
domain = [('name', '=', parsed_result['code'])]
|
||||
obj = self.pool.get('stock.quant.package')
|
||||
id_in_operation = 'package_id'
|
||||
|
||||
matching_product_ids = obj.search(cr, uid, domain, context=context)
|
||||
if matching_product_ids:
|
||||
op_id = stock_operation_obj._search_and_increment(cr, uid, picking_id, [(id_in_operation, '=', matching_product_ids[0])], filter_visible=True, visible_op_ids=visible_op_ids, increment=weight, context=context)
|
||||
answer['operation_id'] = op_id
|
||||
return answer
|
||||
|
||||
#check if the barcode correspond to a lot
|
||||
elif parsed_result['type'] == 'lot':
|
||||
lot_obj = self.pool.get('stock.production.lot')
|
||||
matching_lot_ids = lot_obj.search(cr, uid, [('name', '=', parsed_result['code'])], context=context)
|
||||
if matching_lot_ids:
|
||||
lot = lot_obj.browse(cr, uid, matching_lot_ids[0], context=context)
|
||||
op_id = stock_operation_obj._search_and_increment(cr, uid, picking_id, [('product_id', '=', lot.product_id.id), ('lot_id', '=', lot.id)], filter_visible=True, visible_op_ids=visible_op_ids, increment=1, context=context)
|
||||
answer['operation_id'] = op_id
|
||||
return answer
|
||||
|
||||
#check if the barcode correspond to a location
|
||||
elif parsed_result['type'] == 'location':
|
||||
stock_location_obj = self.pool.get('stock.location')
|
||||
matching_location_ids = stock_location_obj.search(cr, uid, [('barcode', '=', parsed_result['code'])], context=context)
|
||||
if matching_location_ids:
|
||||
#if we have a location, return immediatly with the location name
|
||||
location = stock_location_obj.browse(cr, uid, matching_location_ids[0], context=None)
|
||||
answer['filter_loc'] = stock_location_obj._name_get(cr, uid, location, context=None)
|
||||
answer['filter_loc_id'] = matching_location_ids[0]
|
||||
return answer
|
||||
|
||||
return answer
|
||||
|
||||
|
||||
class stock_production_lot(osv.osv):
|
||||
@@ -1821,26 +1794,10 @@ class stock_move(osv.osv):
|
||||
'warehouse_id': fields.many2one('stock.warehouse', 'Warehouse', help="Technical field depicting the warehouse to consider for the route selection on the next procurement (if any)."),
|
||||
}
|
||||
|
||||
def _default_location_destination(self, cr, uid, context=None):
|
||||
context = context or {}
|
||||
if context.get('default_picking_type_id', False):
|
||||
pick_type = self.pool.get('stock.picking.type').browse(cr, uid, context['default_picking_type_id'], context=context)
|
||||
return pick_type.default_location_dest_id and pick_type.default_location_dest_id.id or False
|
||||
return False
|
||||
|
||||
def _default_location_source(self, cr, uid, context=None):
|
||||
context = context or {}
|
||||
if context.get('default_picking_type_id', False):
|
||||
pick_type = self.pool.get('stock.picking.type').browse(cr, uid, context['default_picking_type_id'], context=context)
|
||||
return pick_type.default_location_src_id and pick_type.default_location_src_id.id or False
|
||||
return False
|
||||
|
||||
def _default_destination_address(self, cr, uid, context=None):
|
||||
return False
|
||||
|
||||
_defaults = {
|
||||
'location_id': _default_location_source,
|
||||
'location_dest_id': _default_location_destination,
|
||||
'partner_id': _default_destination_address,
|
||||
'state': 'draft',
|
||||
'priority': '1',
|
||||
@@ -2108,6 +2065,8 @@ class stock_move(osv.osv):
|
||||
'move_type': move.group_id and move.group_id.move_type or 'direct',
|
||||
'partner_id': move.partner_id.id or False,
|
||||
'picking_type_id': move.picking_type_id and move.picking_type_id.id or False,
|
||||
'location_id': move.location_id.id,
|
||||
'location_dest_id': move.location_dest_id.id,
|
||||
}
|
||||
pick = pick_obj.create(cr, uid, values, context=context)
|
||||
return self.write(cr, uid, move_ids, {'picking_id': pick}, context=context)
|
||||
@@ -2183,7 +2142,9 @@ class stock_move(osv.osv):
|
||||
""" Changes the state to assigned.
|
||||
@return: True
|
||||
"""
|
||||
return self.write(cr, uid, ids, {'state': 'assigned'}, context=context)
|
||||
res = self.write(cr, uid, ids, {'state': 'assigned'}, context=context)
|
||||
self.check_recompute_pack_op(cr, uid, ids, context=context)
|
||||
return res
|
||||
|
||||
def check_tracking_product(self, cr, uid, product, lot_id, location, location_dest, context=None):
|
||||
check = False
|
||||
@@ -2201,9 +2162,24 @@ class stock_move(osv.osv):
|
||||
""" Checks if serial number is assigned to stock move or not and raise an error if it had to.
|
||||
"""
|
||||
self.check_tracking_product(cr, uid, move.product_id, lot_id, move.location_id, move.location_dest_id, context=context)
|
||||
|
||||
|
||||
def action_assign(self, cr, uid, ids, context=None):
|
||||
def check_recompute_pack_op(self, cr, uid, ids, context=None):
|
||||
pickings = list(set([x.picking_id for x in self.browse(cr, uid, ids, context=context) if x.picking_id]))
|
||||
pickings_partial = []
|
||||
pickings_write = []
|
||||
pick_obj = self.pool['stock.picking']
|
||||
for pick in pickings:
|
||||
# Check if someone was treating the picking already
|
||||
if not any([x.qty_done > 0 for x in pick.pack_operation_ids]):
|
||||
pickings_partial.append(pick.id)
|
||||
else:
|
||||
pickings_write.append(pick.id)
|
||||
if pickings_partial:
|
||||
pick_obj.do_prepare_partial(cr, uid, pickings_partial, context=context)
|
||||
if pickings_write:
|
||||
pick_obj.write(cr, uid, pickings_write, {'recompute_pack_op': True}, context=context)
|
||||
|
||||
def action_assign(self, cr, uid, ids, no_prepare=False, context=None):
|
||||
""" Checks the product type and accordingly writes the state.
|
||||
"""
|
||||
context = context or {}
|
||||
@@ -2266,8 +2242,11 @@ class stock_move(osv.osv):
|
||||
quant_obj.quants_reserve(cr, uid, quants, move, context=context)
|
||||
|
||||
#force assignation of consumable products and incoming from supplier/inventory/production
|
||||
# Do not take force_assign as it would create pack operations
|
||||
if to_assign_moves:
|
||||
self.force_assign(cr, uid, to_assign_moves, context=context)
|
||||
self.write(cr, uid, to_assign_moves, {'state': 'assigned'}, context=context)
|
||||
if not no_prepare:
|
||||
self.check_recompute_pack_op(cr, uid, ids, context=context)
|
||||
|
||||
def action_cancel(self, cr, uid, ids, context=None):
|
||||
""" Cancels the moves and if all moves are cancelled it cancels the picking.
|
||||
@@ -3924,6 +3903,8 @@ class stock_pack_operation(osv.osv):
|
||||
_name = "stock.pack.operation"
|
||||
_description = "Packing Operation"
|
||||
|
||||
_order = "result_package_id desc, id"
|
||||
|
||||
def _get_remaining_prod_quantities(self, cr, uid, operation, context=None):
|
||||
'''Get the remaining quantities per product on an operation with a package. This function returns a dictionary'''
|
||||
#if the operation doesn't concern a package, it's not relevant to call this function
|
||||
@@ -3958,9 +3939,11 @@ class stock_pack_operation(osv.osv):
|
||||
|
||||
def product_id_change(self, cr, uid, ids, product_id, product_uom_id, product_qty, context=None):
|
||||
res = self.on_change_tests(cr, uid, ids, product_id, product_uom_id, product_qty, context=context)
|
||||
if product_id and not product_uom_id:
|
||||
product = self.pool.get('product.product').browse(cr, uid, product_id, context=context)
|
||||
uom_obj = self.pool['product.uom']
|
||||
product = self.pool.get('product.product').browse(cr, uid, product_id, context=context)
|
||||
if product_id and not product_uom_id or uom_obj.browse(cr, uid, product_uom_id, context=context).category_id.id != product.uom_id.category_id.id:
|
||||
res['value']['product_uom_id'] = product.uom_id.id
|
||||
res['domain'] = {'product_uom': [('category_id','=',product.uom_id.category_id.id)]}
|
||||
return res
|
||||
|
||||
def on_change_tests(self, cr, uid, ids, product_id, product_uom_id, product_qty, context=None):
|
||||
@@ -3984,12 +3967,38 @@ class stock_pack_operation(osv.osv):
|
||||
}
|
||||
return res
|
||||
|
||||
def _compute_location_description(self, cr, uid, ids, field_name, arg, context=None):
|
||||
res = {}
|
||||
for op in self.browse(cr, uid, ids, context=context):
|
||||
from_name = op.location_id.name
|
||||
to_name = op.location_dest_id.name
|
||||
if op.package_id and op.product_id:
|
||||
from_name += " : " + op.package_id.name
|
||||
if op.result_package_id:
|
||||
to_name += " : " + op.result_package_id.name
|
||||
res[op.id] = {'from_loc': from_name,
|
||||
'to_loc': to_name}
|
||||
return res
|
||||
|
||||
def _get_bool(self, cr, uid, ids, field_name, arg, context=None):
|
||||
res = {}
|
||||
for pack in self.browse(cr, uid, ids, context=context):
|
||||
res[pack.id] = (pack.qty_done > 0.0)
|
||||
return res
|
||||
|
||||
def _set_processed_qty(self, cr, uid, id, field_name, field_value, arg, context=None):
|
||||
op = self.browse(cr, uid, id, context=context)
|
||||
if field_value and op.qty_done == 0:
|
||||
self.write(cr, uid, [id], {'qty_done': 1.0}, context=context)
|
||||
return True
|
||||
|
||||
_columns = {
|
||||
'picking_id': fields.many2one('stock.picking', 'Stock Picking', help='The stock operation where the packing has been made', required=True),
|
||||
'product_id': fields.many2one('product.product', 'Product', ondelete="CASCADE"), # 1
|
||||
'product_uom_id': fields.many2one('product.uom', 'Product Unit of Measure'),
|
||||
'product_qty': fields.float('Quantity', digits_compute=dp.get_precision('Product Unit of Measure'), required=True),
|
||||
'qty_done': fields.float('Quantity Processed', digits_compute=dp.get_precision('Product Unit of Measure')),
|
||||
'product_uom_id': fields.many2one('product.uom', 'Unit of Measure'),
|
||||
'product_qty': fields.float('To Do', digits_compute=dp.get_precision('Product Unit of Measure'), required=True),
|
||||
'qty_done': fields.float('Processed', digits_compute=dp.get_precision('Product Unit of Measure')),
|
||||
'processed_boolean': fields.function(_get_bool, fnct_inv=_set_processed_qty, type='boolean', string='Processed'),
|
||||
'package_id': fields.many2one('stock.quant.package', 'Source Package'), # 2
|
||||
'lot_id': fields.many2one('stock.production.lot', 'Lot/Serial Number'),
|
||||
'result_package_id': fields.many2one('stock.quant.package', 'Destination Package', help="If set, the operations are packed into this package", required=False, ondelete='cascade'),
|
||||
@@ -4002,16 +4011,30 @@ class stock_pack_operation(osv.osv):
|
||||
'remaining_qty': fields.function(_get_remaining_qty, type='float', digits = 0, string="Remaining Qty", help="Remaining quantity in default UoM according to moves matched with this operation. "),
|
||||
'location_id': fields.many2one('stock.location', 'Source Location', required=True),
|
||||
'location_dest_id': fields.many2one('stock.location', 'Destination Location', required=True),
|
||||
'processed': fields.selection([('true','Yes'), ('false','No')],'Has been processed?', required=True),
|
||||
'from_loc': fields.function(_compute_location_description, type='char', string='From', multi='loc', readonly=True),
|
||||
'to_loc': fields.function(_compute_location_description, type='char', string='To', multi='loc', readonly=True),
|
||||
'fresh_record': fields.boolean('Newly created pack operation'),
|
||||
'state': fields.related('picking_id', 'state', type='selection', selection=[
|
||||
('draft', 'Draft'),
|
||||
('cancel', 'Cancelled'),
|
||||
('waiting', 'Waiting Another Operation'),
|
||||
('confirmed', 'Waiting Availability'),
|
||||
('partially_available', 'Partially Available'),
|
||||
('assigned', 'Ready to Transfer'),
|
||||
('done', 'Transferred'),
|
||||
]),
|
||||
}
|
||||
|
||||
_defaults = {
|
||||
'date': fields.date.context_today,
|
||||
'qty_done': 0,
|
||||
'processed': lambda *a: 'false',
|
||||
'qty_done': 0.0,
|
||||
'product_qty': 0.0,
|
||||
'processed_boolean': lambda *a: False,
|
||||
'fresh_record': True,
|
||||
}
|
||||
|
||||
def write(self, cr, uid, ids, vals, context=None):
|
||||
vals['fresh_record'] = False
|
||||
context = context or {}
|
||||
res = super(stock_pack_operation, self).write(cr, uid, ids, vals, context=context)
|
||||
if isinstance(ids, (int, long)):
|
||||
@@ -4028,6 +4051,11 @@ class stock_pack_operation(osv.osv):
|
||||
self.pool.get("stock.picking").do_recompute_remaining_quantities(cr, uid, [vals['picking_id']], context=context)
|
||||
return res_id
|
||||
|
||||
def unlink(self, cr, uid, ids, context=None):
|
||||
if any([x.state in ('done', 'cancel') for x in self.browse(cr, uid, ids, context=context)]):
|
||||
raise UserError(_('You can not delete pack operations of a done picking'))
|
||||
return super(stock_pack_operation, self).unlink(cr, uid, ids, context=context)
|
||||
|
||||
def action_drop_down(self, cr, uid, ids, context=None):
|
||||
''' Used by barcode interface to say that pack_operation has been moved from src location
|
||||
to destination location, if qty_done is less than product_qty than we have to split the
|
||||
@@ -4406,6 +4434,7 @@ class stock_picking_type(osv.osv):
|
||||
'default_location_dest_id': fields.many2one('stock.location', 'Default Destination Location'),
|
||||
'code': fields.selection([('incoming', 'Suppliers'), ('outgoing', 'Customers'), ('internal', 'Internal')], 'Type of Operation', required=True),
|
||||
'return_picking_type_id': fields.many2one('stock.picking.type', 'Picking Type for Returns'),
|
||||
'show_entire_packs': fields.boolean('Show entire packs to move'),
|
||||
'warehouse_id': fields.many2one('stock.warehouse', 'Warehouse', ondelete='cascade'),
|
||||
'active': fields.boolean('Active'),
|
||||
|
||||
|
||||
+46
-12
@@ -66,6 +66,8 @@
|
||||
picking_type_id: stock.picking_type_out
|
||||
origin: 'outgoing shipment main_warehouse'
|
||||
partner_id: base.res_partner_6
|
||||
location_id: stock.stock_location_stock
|
||||
location_dest_id: stock.stock_location_customers
|
||||
move_lines:
|
||||
- product_id: product.product_product_27
|
||||
product_uom: product.product_uom_unit
|
||||
@@ -79,6 +81,8 @@
|
||||
origin: 'outgoing shipment'
|
||||
partner_id: base.res_partner_6
|
||||
date: !eval "'%s-%s-15' %((datetime.now()-timedelta(days=60)).year, (datetime.now()-timedelta(days=60)).month)"
|
||||
location_id: stock.stock_location_stock
|
||||
location_dest_id: stock.stock_location_customers
|
||||
move_lines:
|
||||
- product_id: product.product_product_6
|
||||
product_uom: product.product_uom_unit
|
||||
@@ -98,6 +102,8 @@
|
||||
origin: 'outgoing shipment your_company warehouse'
|
||||
partner_id: base.res_partner_6
|
||||
date: !eval "'%s-%s-15' % ((datetime.now()-timedelta(days=30)).month,(datetime.now()-timedelta(days=30)).month)"
|
||||
location_id: stock.stock_location_stock
|
||||
location_dest_id: stock.stock_location_customers
|
||||
move_lines:
|
||||
- product_id: product.product_product_31
|
||||
product_uom: product.product_uom_unit
|
||||
@@ -110,6 +116,8 @@
|
||||
picking_type_id: stock.picking_type_out
|
||||
origin: 'your company warehouse'
|
||||
partner_id: base.res_partner_6
|
||||
location_id: stock.stock_location_stock
|
||||
location_dest_id: stock.stock_location_customers
|
||||
move_lines:
|
||||
- product_id: product.product_product_5
|
||||
product_uom: product.product_uom_unit
|
||||
@@ -123,9 +131,10 @@
|
||||
!record {model: stock.picking, id: incomming_shipment}:
|
||||
picking_type_id: stock.picking_type_in
|
||||
origin: 'incoming_shipment for test'
|
||||
location_id: stock_location_suppliers
|
||||
location_dest_id: stock_location_14
|
||||
-
|
||||
!record {model: stock.move, id: incomming_shipment_icecream}:
|
||||
picking_id: stock.incomming_shipment
|
||||
product_id: product_icecream
|
||||
product_uom: product.product_uom_kgm
|
||||
product_uom_qty: 50.0
|
||||
@@ -137,6 +146,8 @@
|
||||
picking_type_id: stock.picking_type_in
|
||||
origin: 'incoming_shipment'
|
||||
partner_id: base.res_partner_6
|
||||
location_id: stock.stock_location_suppliers
|
||||
location_dest_id: stock.stock_location_stock
|
||||
move_lines:
|
||||
- product_id: product.product_product_48
|
||||
product_uom: product.product_uom_unit
|
||||
@@ -149,6 +160,8 @@
|
||||
picking_type_id: stock.picking_type_in
|
||||
origin: 'incoming_shipment main_warehouse'
|
||||
partner_id: base.res_partner_6
|
||||
location_id: stock.stock_location_suppliers
|
||||
location_dest_id: stock.stock_location_stock
|
||||
move_lines:
|
||||
- product_id: product.product_product_22
|
||||
product_uom: product.product_uom_unit
|
||||
@@ -161,6 +174,8 @@
|
||||
picking_type_id: stock.picking_type_in
|
||||
origin: 'incoming_shipment your_company warehouse'
|
||||
partner_id: base.res_partner_6
|
||||
location_id: stock.stock_location_suppliers
|
||||
location_dest_id: stock.stock_location_stock
|
||||
move_lines:
|
||||
- product_id: product.product_product_10
|
||||
product_uom: product.product_uom_unit
|
||||
@@ -170,16 +185,18 @@
|
||||
location_dest_id: stock.stock_location_stock
|
||||
-
|
||||
!record {model: stock.picking, id: incomming_shipment4}:
|
||||
picking_type_id: stock.picking_type_in
|
||||
origin: 'incoming_shipment_main_warehouse'
|
||||
partner_id: base.res_partner_6
|
||||
move_lines:
|
||||
- product_id: product.product_product_31
|
||||
product_uom: product.product_uom_unit
|
||||
product_uom_qty: 50.0
|
||||
picking_type_id: stock.picking_type_in
|
||||
location_id: stock.stock_location_suppliers
|
||||
location_dest_id: stock.stock_location_stock
|
||||
picking_type_id: stock.picking_type_in
|
||||
origin: 'incoming_shipment_main_warehouse'
|
||||
partner_id: base.res_partner_6
|
||||
location_id: stock.stock_location_suppliers
|
||||
location_dest_id: stock.stock_location_stock
|
||||
move_lines:
|
||||
- product_id: product.product_product_31
|
||||
product_uom: product.product_uom_unit
|
||||
product_uom_qty: 50.0
|
||||
picking_type_id: stock.picking_type_in
|
||||
location_id: stock.stock_location_suppliers
|
||||
location_dest_id: stock.stock_location_stock
|
||||
-
|
||||
Create STOCK_PICKING_IN for Chicago Warehouse
|
||||
-
|
||||
@@ -187,6 +204,8 @@
|
||||
picking_type_id: chi_picking_type_in
|
||||
origin: 'incoming_chicago_warehouse'
|
||||
partner_id: base.res_partner_6
|
||||
location_id: stock.stock_location_suppliers
|
||||
location_dest_id: stock.stock_location_shop0
|
||||
move_lines:
|
||||
- product_id: product.product_product_9
|
||||
product_uom: product.product_uom_unit
|
||||
@@ -194,11 +213,14 @@
|
||||
picking_type_id: chi_picking_type_in
|
||||
location_id: stock.stock_location_suppliers
|
||||
location_dest_id: stock.stock_location_shop0
|
||||
|
||||
-
|
||||
!record {model: stock.picking, id: incomming_chicago_warehouse1}:
|
||||
picking_type_id: chi_picking_type_in
|
||||
origin: 'incoming_shipment_chicago_warehouse'
|
||||
partner_id: base.res_partner_6
|
||||
location_id: stock.stock_location_suppliers
|
||||
location_dest_id: stock.stock_location_shop0
|
||||
move_lines:
|
||||
- product_id: product.product_product_48
|
||||
product_uom: product.product_uom_unit
|
||||
@@ -218,6 +240,8 @@
|
||||
picking_type_id: chi_picking_type_in
|
||||
origin: 'incoming_shipment chicago_warehouse'
|
||||
partner_id: base.res_partner_6
|
||||
location_id: stock.stock_location_suppliers
|
||||
location_dest_id: stock.stock_location_shop0
|
||||
move_lines:
|
||||
- product_id: product.product_product_32
|
||||
product_uom: product.product_uom_unit
|
||||
@@ -231,6 +255,8 @@
|
||||
origin: 'chicago_warehouse'
|
||||
partner_id: base.res_partner_6
|
||||
date: !eval "'%s-%s-2' % ((datetime.now()-timedelta(days=30)).month,(datetime.now()-timedelta(days=30)).month)"
|
||||
location_id: stock.stock_location_suppliers
|
||||
location_dest_id: stock.stock_location_shop0
|
||||
move_lines:
|
||||
- product_id: product.product_product_22
|
||||
product_uom: product.product_uom_unit
|
||||
@@ -245,6 +271,8 @@
|
||||
picking_type_id: chi_picking_type_out
|
||||
origin: 'outgoing_chicago_warehouse'
|
||||
partner_id: base.res_partner_6
|
||||
location_id: stock.stock_location_shop0
|
||||
location_dest_id: stock.stock_location_customers
|
||||
move_lines:
|
||||
- product_id: product.product_product_27
|
||||
product_uom: product.product_uom_unit
|
||||
@@ -257,6 +285,8 @@
|
||||
picking_type_id: chi_picking_type_out
|
||||
origin: 'outgoing_shipment_chicago_warehouse'
|
||||
partner_id: base.res_partner_6
|
||||
location_id: stock.stock_location_shop0
|
||||
location_dest_id: stock.stock_location_customers
|
||||
move_lines:
|
||||
- product_id: product.product_product_6
|
||||
product_uom: product.product_uom_unit
|
||||
@@ -275,6 +305,8 @@
|
||||
picking_type_id: chi_picking_type_out
|
||||
origin: 'chicago_warehouse'
|
||||
partner_id: base.res_partner_6
|
||||
location_id: stock.stock_location_shop0
|
||||
location_dest_id: stock.stock_location_customers
|
||||
move_lines:
|
||||
- product_id: product.product_product_31
|
||||
product_uom: product.product_uom_unit
|
||||
@@ -286,6 +318,8 @@
|
||||
!record {model: stock.picking, id: outgoing_chicago_warehouse3}:
|
||||
picking_type_id: chi_picking_type_out
|
||||
origin: 'outgoing chicago warehouse'
|
||||
location_id: stock.stock_location_shop0
|
||||
location_dest_id: stock.stock_location_customers
|
||||
partner_id: base.res_partner_6
|
||||
move_lines:
|
||||
- product_id: product.product_product_5
|
||||
@@ -302,4 +336,4 @@
|
||||
record_chi_out = self.browse(cr, uid, ref('outgoing_chicago_warehouse1'), context=context)
|
||||
if record.state != 'done' and record_chi_in.state != 'done' and record_chi_out.state != 'done':
|
||||
self.force_assign(cr, uid, [ref('outgoing_shipment_main_warehouse'),ref('outgoing_chicago_warehouse3'),ref('outgoing_shipment_main_warehouse2')], context=context)
|
||||
self.do_transfer(cr, uid, [ref('outgoing_shipment_main_warehouse1'),ref('incomming_chicago_warehouse1'),ref('outgoing_chicago_warehouse1')], context=context)
|
||||
self.do_transfer(cr, uid, [ref('outgoing_shipment_main_warehouse1'),ref('incomming_chicago_warehouse1'),ref('outgoing_chicago_warehouse1')], context=context)
|
||||
+90
-72
@@ -689,20 +689,21 @@
|
||||
<form string="Transfer">
|
||||
<header>
|
||||
<button name="action_confirm" states="draft" string="Mark as Todo" type="object" class="oe_highlight" groups="base.group_user"/>
|
||||
<button name="action_assign" states="confirmed" string="Check Availability" type="object" class="oe_highlight" groups="base.group_user"/>
|
||||
<button name="rereserve_pick" states="partially_available" string="Recheck Availability" type="object" class="oe_highlight" groups="base.group_user"/>
|
||||
<button name="do_new_transfer" states="draft,partially_available,assigned" string="Validate" groups="stock.group_stock_user" type="object" class="oe_highlight"/>
|
||||
<button name="action_assign" states="confirmed,waiting" string="Reserve" type="object" class="oe_highlight" groups="base.group_user"/>
|
||||
<button name="rereserve_pick" states="partially_available" string="Recheck Reservation" type="object" class="oe_highlight" groups="base.group_user"/>
|
||||
<button name="force_assign" states="confirmed,waiting,partially_available" string="Force Availability" type="object" groups="base.group_user"/>
|
||||
<button name="do_enter_transfer_details" states="assigned,partially_available" string="Transfer" groups="stock.group_stock_user" type="object" class="oe_highlight"/>
|
||||
<button name="do_print_picking" string="Print Picking List" groups="stock.group_stock_user" type="object" attrs="{'invisible': ['|', ('picking_type_code', '=', 'outgoing'), ('state', '!=', 'assigned')]}"/>
|
||||
<button name="do_print_picking_operations" string="Print Picking List" groups="stock.group_stock_user" type="object" attrs="{'invisible': ['|', ('picking_type_code', '=', 'outgoing'), ('state', 'not in', ('assigned', 'partially_available'))]}"/>
|
||||
<button name="%(act_stock_return_picking)d" string="Reverse Transfer" states="done" type="action" groups="base.group_user"/>
|
||||
<button name="action_cancel" states="assigned,confirmed,partially_available,draft,waiting" string="Cancel Transfer" groups="base.group_user" type="object"/>
|
||||
<button name="do_unreserve" string="Unreserve" groups="base.group_user" type="object" attrs="{'invisible': [('quant_reserved_exist', '=', False)]}"/>
|
||||
<button name="do_prepare_partial" attrs="{'invisible': ['|', ('launch_pack_operations', '=', True), '|', ('state', 'not in', ('assigned', 'partially_available')), ('pack_operation_ids', '!=', [])]}"
|
||||
string="Proceed..." type="object"/>
|
||||
<field name="launch_pack_operations" invisible="1"/>
|
||||
<field name="state" widget="statusbar" statusbar_visible="draft,confirmed,partially_available,assigned,done" statusbar_colors='{"shipping_except":"red","invoice_except":"red","waiting_date":"blue"}'/>
|
||||
</header>
|
||||
<sheet>
|
||||
<div class="oe_right oe_button_box">
|
||||
<button name="do_partial_open_barcode" groups="stock.group_stock_user" type="object" class="oe_stock_scan_button" attrs="{'invisible': ['|',('pack_operation_exist', '=', True),('state','not in',('assigned', 'partially_available'))]}"><img src="/stock/static/src/img/scan.png" class="oe_stock_scan_image oe_stock_scan_image_btn"/></button>
|
||||
<button name="open_barcode_interface" groups="stock.group_stock_user" type="object" class="oe_stock_scan_button" attrs="{'invisible': ['|',('pack_operation_exist', '=', False),('state','not in',('assigned', 'partially_available'))]}"><img src="/stock/static/src/img/scan.png" class="oe_stock_scan_image oe_stock_scan_image_btn"/></button>
|
||||
<div class="oe_right oe_button_box" name="button_box">
|
||||
</div>
|
||||
<h1>
|
||||
<field name="name" class="oe_inline" attrs="{'invisible': [('name','=','/')]}" readonly="1"/>
|
||||
@@ -710,12 +711,12 @@
|
||||
<group>
|
||||
<group>
|
||||
<field name="partner_id"/>
|
||||
<field name="location_id" groups="stock.group_locations" attrs="{'invisible': [('picking_type_code', '=', 'incoming')]}"/>
|
||||
<field name="location_dest_id" groups="stock.group_locations" attrs="{'invisible': [('picking_type_code', '=', 'outgoing')]}"/>
|
||||
<field name="backorder_id" readonly="1" attrs="{'invisible': [('backorder_id','=',False)]}"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="date"/>
|
||||
<field name="min_date"/>
|
||||
<field name="date_done"/>
|
||||
<field name="origin" placeholder="e.g. PO0032" class="oe_inline"/>
|
||||
<label for="owner_id" groups="stock.group_tracking_owner"/>
|
||||
<div groups="stock.group_tracking_owner">
|
||||
@@ -726,10 +727,53 @@
|
||||
</group>
|
||||
</group>
|
||||
<notebook>
|
||||
<page string="Products">
|
||||
<separator string="Stock Moves" attrs="{'invisible': [('pack_operation_exist', '=', False)]}"/>
|
||||
<field name="move_lines" context="{'address_in_id': partner_id, 'form_view_ref':'stock.view_move_picking_form', 'tree_view_ref':'stock.view_move_picking_tree', 'default_picking_type_id': picking_type_id}">
|
||||
<kanban class="o_kanban_mobile o_kanban_one2many">
|
||||
<page string="Operations" attrs="{'invisible': [('state', 'in', ('draft', 'confirmed', 'waiting'))]}">
|
||||
<button name="do_prepare_partial" type="object" string="Recompute" attrs="{'invisible': [('recompute_pack_op','=', False)]}"/>
|
||||
<field name="recompute_pack_op" invisible="1"/>
|
||||
<field name="pack_operation_ids" invisible="1"/>
|
||||
<field name="pack_operation_product_ids" context="{'default_picking_id': id, 'default_location_id': location_id, 'default_location_dest_id': location_dest_id}">
|
||||
<tree editable="bottom" colors="grey:result_package_id;red:qty_done>product_qty;green:qty_done==product_qty and state!='done'">
|
||||
<field name="package_id" groups="stock.group_tracking_lot" invisible="1"/>
|
||||
<field name="product_id" on_change="product_id_change(product_id, product_uom_id, product_qty)" required="1" attrs="{'readonly': [('fresh_record', '=', False)]}"/>
|
||||
<field name="fresh_record" invisible="1"/>
|
||||
<field name="product_uom_id" attrs="{'readonly': [('fresh_record', '=', False)]}" groups="product.group_uom"/>
|
||||
<field name="lot_id" domain="[('product_id','=?', product_id)]" context="{'product_id': product_id}" groups="stock.group_production_lot" attrs="{'readonly': [('fresh_record', '=', False)]}"/>
|
||||
<field name="picking_id" invisible="1"/>
|
||||
<field name="owner_id" groups="stock.group_tracking_owner"/>
|
||||
<field name="location_id" domain="[('id', 'child_of', parent.location_id)]" invisible="1"/>
|
||||
<field name="location_dest_id" domain="[('id', 'child_of', parent.location_dest_id)]" invisible="1"/>
|
||||
<field name="from_loc" groups="stock.group_locations,stock.group_tracking_lot"/>
|
||||
<field name="to_loc" groups="stock.group_locations,stock.group_tracking_lot"/>
|
||||
<field name="result_package_id" groups="stock.group_tracking_lot" context="{'location_id': location_dest_id}" invisible="1"/>
|
||||
<field name="state" invisible="1"/>
|
||||
<field name="qty_done"/>
|
||||
<field name="product_qty" readonly="1" attrs="{'required': [('product_id', '!=', False)]}"/>
|
||||
<button name="%(stock.pack_details)d" string="Modify" type="action" icon="STOCK_EXECUTE"
|
||||
states="confirmed,assigned,waiting,partially_available"/>
|
||||
</tree>
|
||||
</field>
|
||||
<button class="oe_link oe_right" name="put_in_pack" type="object" string="↳Put in Pack" attrs="{'invisible': [('state', 'in', ('done', 'cancel'))]}" groups="stock.group_tracking_lot"/>
|
||||
<field name="picking_type_entire_packs" invisible="1"/>
|
||||
<field name="pack_operation_pack_ids" attrs="{'invisible': [('pack_operation_pack_ids', '=', []), ('picking_type_entire_packs', '=', False)]}" context="{'default_picking_id': id, 'default_location_id': location_id, 'default_location_dest_id': location_dest_id, 'default_picking_id': id}" groups="stock.group_tracking_lot">
|
||||
<tree editable="bottom" colors="grey:qty_done>0;">
|
||||
<field name="package_id" required="1" string="Package To Move"/>
|
||||
<field name="picking_id" invisible="1"/>
|
||||
<field name="location_id" domain="[('id', 'child_of', parent.location_id)]" invisible="1"/>
|
||||
<field name="from_loc" groups="stock.group_locations,stock.group_tracking_lot"/>
|
||||
<field name="to_loc" groups="stock.group_locations,stock.group_tracking_lot"/>
|
||||
<field name="location_dest_id" domain="[('id', 'child_of', parent.location_dest_id)]" invisible="1"/>
|
||||
<field name="result_package_id" groups="stock.group_tracking_lot" context="{'location_id': location_dest_id}" invisible="1"/>
|
||||
<field name="state" invisible="1"/>
|
||||
<field name="qty_done" invisible="1"/>
|
||||
<field name="processed_boolean"/>
|
||||
<button name="%(stock.pack_details)d" string="Modify" type="action" icon="STOCK_EXECUTE"
|
||||
states="confirmed,assigned,waiting,partially_available"/>
|
||||
</tree>
|
||||
</field>
|
||||
</page>
|
||||
<page string="Initial Demand" attrs="{'invisible': [('state', 'not in', ('draft', 'confirmed', 'waiting'))]}">
|
||||
<field name="move_lines" context="{'address_in_id': partner_id, 'form_view_ref':'stock.view_move_picking_form', 'tree_view_ref':'stock.view_move_picking_tree', 'default_picking_type_id': picking_type_id, 'default_location_id': location_id, 'default_location_dest_id': location_dest_id}">
|
||||
<kanban class="o_kanban_mobile o_kanban_one2many">
|
||||
<field name="product_id"/>
|
||||
<field name="availability"/>
|
||||
<field name="product_uom_qty"/>
|
||||
@@ -759,34 +803,16 @@
|
||||
<field name="pack_operation_exist" invisible="1"/>
|
||||
<field name="note" placeholder="Add an internal note..." class="oe_inline"/>
|
||||
</page>
|
||||
<page string="Operations" attrs="{'invisible': ['|', ('state','!=','done'), ('pack_operation_ids','=',[])]}">
|
||||
<field name="pack_operation_ids" context="{'default_location_id': location_id, 'default_location_dest_id': location_dest_id}">
|
||||
<tree editable="top">
|
||||
<field name="package_id" groups="stock.group_tracking_lot"/>
|
||||
<field name="product_id" on_change="product_id_change(product_id, product_uom_id, product_qty)"/>
|
||||
<field name="product_uom_id" groups="product.group_uom"/>
|
||||
<field name="lot_id" domain="[('product_id','=?', product_id)]" context="{'product_id': product_id}" groups="stock.group_production_lot"/>
|
||||
<field name="picking_id" invisible="1"/>
|
||||
<field name="owner_id" groups="stock.group_tracking_owner"/>
|
||||
<field name="product_qty" attrs="{'required': [('product_id', '!=', False)]}"/>
|
||||
<field name="location_id" domain="[('id', 'child_of', parent.location_id)]"/>
|
||||
<field name="location_dest_id" domain="[('id', 'child_of', parent.location_dest_id)]"/>
|
||||
<field name="result_package_id" groups="stock.group_tracking_lot" context="{'location_id': location_dest_id}"/>
|
||||
</tree>
|
||||
</field>
|
||||
<p class="oe_grey" groups="stock.group_tracking_lot">
|
||||
If there is no product but a source package, this means the source package was moved entirely. If there is a product and a source package, the product was taken from the source package.
|
||||
</p>
|
||||
<page string="Initial Demand" attrs="{'invisible': [('state', 'in', ('draft', 'confirmed', 'waiting', 'done'))]}">
|
||||
<field name="move_lines_related" readonly="1"/>
|
||||
</page>
|
||||
<page string="Additional Info">
|
||||
<group string="General Informations">
|
||||
<page string="Additional Info" name="extra">
|
||||
<group>
|
||||
<group>
|
||||
<field name="move_type"/>
|
||||
<field name="picking_type_id"/>
|
||||
<field name="picking_type_id" on_change="onchange_picking_type(picking_type_id)"/>
|
||||
<field name="picking_type_code" invisible="1"/>
|
||||
<field name="quant_reserved_exist" invisible="1"/>
|
||||
<field name="location_id" invisible="1"/>
|
||||
<field name="location_dest_id" invisible="1"/>
|
||||
<field name="quant_reserved_exist" invisible="1"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="company_id" groups="base.group_multi_company" options="{'no_create': True}"/>
|
||||
@@ -1049,8 +1075,6 @@
|
||||
<field name="date" groups="base.group_no_one"/>
|
||||
<field name="date_expected"/>
|
||||
<field name="state"/>
|
||||
<button name="action_done" states="confirmed,assigned" string="Process" type="object"
|
||||
groups="stock.group_stock_user" icon="gtk-go-forward" help="Done"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
@@ -1060,14 +1084,15 @@
|
||||
<field name="model">stock.move</field>
|
||||
<field eval="4" name="priority"/>
|
||||
<field name="arch" type="xml">
|
||||
<tree decoration-muted="scrapped == True" string="Stock Moves">
|
||||
<field name="product_id"/>
|
||||
<tree decoration-muted="scrapped == True" colors="grey:scrapped == True" string="Stock Moves" editable="bottom">
|
||||
<field name="product_id" on_change="onchange_product_id(product_id,location_id,location_dest_id, parent.partner_id)"/>
|
||||
<field name="name" invisible="1"/>
|
||||
<field name="product_uom_qty" on_change="onchange_quantity(product_id, product_uom_qty, product_uom, product_uos)"/>
|
||||
<field name="product_uom" string="Unit of Measure" groups="product.group_uom"/>
|
||||
<field name="product_uos_qty" groups="product.group_uos"/>
|
||||
<field name="product_uos" groups="product.group_uos"/>
|
||||
<field name="location_id" groups="stock.group_locations" invisible="1"/>
|
||||
<field name="picking_id" invisible="1" />
|
||||
<field name="product_uos_qty" groups="product.group_uos" invisible="1"/>
|
||||
<field name="product_uos" groups="product.group_uos" invisible="1"/>
|
||||
<field name="location_id" invisible="1"/>
|
||||
<field name="picking_id" invisible="1"/>
|
||||
<field name="create_date" invisible="1" />
|
||||
<field name="date_expected" invisible="1" />
|
||||
<button name="%(stock.move_scrap)d"
|
||||
@@ -1078,9 +1103,8 @@
|
||||
<field name="scrapped" invisible="1"/>
|
||||
<field name="availability" invisible="1"/>
|
||||
<field name="reserved_availability" invisible="1"/>
|
||||
<field name="location_dest_id" groups="stock.group_locations"/>
|
||||
<field name="location_dest_id" invisible="1"/>
|
||||
<field name="remaining_qty" invisible="1"/>
|
||||
<field name="string_availability_info"/>
|
||||
<field name="state"/>
|
||||
</tree>
|
||||
</field>
|
||||
@@ -1093,10 +1117,6 @@
|
||||
<field name="arch" type="xml">
|
||||
<form string="Stock Moves">
|
||||
<header>
|
||||
<button name="action_confirm" states="draft" string="Process Later" type="object" class="oe_highlight"/>
|
||||
<button name="action_done" states="draft,assigned,confirmed" string="Process Entirely" type="object" class="oe_highlight"/>
|
||||
<button name="force_assign" states="confirmed" string="Set Available" type="object" class="oe_highlight"/>
|
||||
<button name="action_cancel" states="draft,assigned,confirmed" string="Cancel Move" type="object"/>
|
||||
<field name="state" widget="statusbar" statusbar_visible="draft,confirmed,assigned,done" statusbar_colors='{"waiting":"blue","confirmed":"blue"}'/>
|
||||
</header>
|
||||
<sheet>
|
||||
@@ -1146,7 +1166,7 @@
|
||||
<field name="move_dest_id" groups="base.group_no_one" readonly="1"/>
|
||||
</group>
|
||||
<group name="quants_grp" string="Reserved Quants" colspan="4" groups="base.group_no_one">
|
||||
<field name="reserved_quant_ids" nolabel="1"/>
|
||||
<field name="string_availability_info"/>
|
||||
</group>
|
||||
</group>
|
||||
</sheet>
|
||||
@@ -1161,15 +1181,11 @@
|
||||
<field name="arch" type="xml">
|
||||
<form string="Stock Moves">
|
||||
<header>
|
||||
<button name="force_assign" states="confirmed" string="Force Availability" type="object" groups="base.group_user"/>
|
||||
<button name="action_confirm" states="draft" string="Confirm" type="object" groups="base.group_user"/>
|
||||
<button name="do_unreserve" states="assigned" string="Cancel Availability" type="object" groups="base.group_user"/>
|
||||
<field name="state" widget="statusbar" statusbar_visible="draft,assigned,done"/>
|
||||
<field name="state" widget="statusbar"/>
|
||||
</header>
|
||||
<group>
|
||||
<group>
|
||||
<group string="#Products">
|
||||
<field name="product_id" on_change="onchange_product_id(product_id,location_id,location_dest_id, parent.partner_id)"/>
|
||||
<field name="procure_method" attrs="{'readonly': [('state', '!=', 'draft')]}" groups="stock.group_adv_location"/>
|
||||
<field name="picking_type_id" invisible="1"/>
|
||||
<label for="product_uom_qty"/>
|
||||
<div>
|
||||
@@ -1181,27 +1197,28 @@
|
||||
states="draft,waiting,confirmed,assigned"
|
||||
groups="base.group_user"/>
|
||||
</div>
|
||||
<label for="product_uos_qty" groups="product.group_uos"/>
|
||||
<div groups="product.group_uos">
|
||||
<label for="product_uos_qty" groups="product.group_uos" invisible="1"/>
|
||||
<div groups="product.group_uos" invisible="1">
|
||||
<field name="product_uos_qty" on_change="onchange_uos_quantity(product_id, product_uos_qty, product_uos, product_uom)" class="oe_inline"/>
|
||||
<field name="product_uos" on_change="onchange_quantity(product_id, product_uom_qty, product_uom, product_uos)" class="oe_inline"/>
|
||||
</div>
|
||||
<field name="name"/>
|
||||
<field name="product_tmpl_id" invisible="1"/>
|
||||
<field name="product_packaging" groups="product.group_stock_packaging" domain="[('product_tmpl_id','=',product_tmpl_id)]"/>
|
||||
</group>
|
||||
<group>
|
||||
<group string="Locations" groups="base.group_no_one">
|
||||
<field name="name"/>
|
||||
<field name="location_id"/>
|
||||
<field name="location_dest_id"/>
|
||||
</group>
|
||||
</group>
|
||||
<group>
|
||||
<group string="Conditions">
|
||||
<field name="procure_method" attrs="{'readonly': [('state', '!=', 'draft')]}" groups="stock.group_adv_location"/>
|
||||
<field name="create_date" invisible="1"/>
|
||||
<field name="date"/>
|
||||
<field name="date_expected" on_change="onchange_date(date,date_expected)"/>
|
||||
<field name="date_expected"/>
|
||||
<field name="move_dest_id" groups="base.group_no_one" readonly="1"/>
|
||||
</group>
|
||||
<group string="Locations" groups="stock.group_locations">
|
||||
<field name="location_id" domain="[('usage','<>','view')]"/>
|
||||
<field name="location_dest_id" domain="[('usage','<>','view')]"/>
|
||||
</group>
|
||||
<group name="quants_grp" string="Reserved Quants" colspan="4" groups="base.group_no_one">
|
||||
<field name="reserved_quant_ids"/>
|
||||
<group name="quants_grp" string="Reserved">
|
||||
<field name="string_availability_info"/>
|
||||
</group>
|
||||
</group>
|
||||
</form>
|
||||
@@ -1436,6 +1453,7 @@
|
||||
<field name="code" on_change="onchange_picking_code(code)"/>
|
||||
<field name="return_picking_type_id"/>
|
||||
<field name="barcode_nomenclature_id"/>
|
||||
<field name="show_entire_packs" groups="stock.group_tracking_lot"/>
|
||||
</group>
|
||||
</group>
|
||||
<separator string="Locations"/>
|
||||
@@ -1767,7 +1785,7 @@
|
||||
<field name="model">stock.quant</field>
|
||||
<field eval="10" name="priority"/>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Quants" create="0">
|
||||
<tree string="Quants" create="0" delete="0">
|
||||
<field name="product_id"/>
|
||||
<field name="qty"/>
|
||||
<field name="product_uom_id" groups="product.group_uom"/>
|
||||
|
||||
@@ -31,7 +31,6 @@
|
||||
-
|
||||
!python {model: stock.picking}: |
|
||||
self.action_confirm(cr, uid, [ref('pick_neg')], context=context)
|
||||
self.do_prepare_partial(cr, uid, [ref('pick_neg')], context=context)
|
||||
-
|
||||
Put 120 pieces on Palneg 1 (package), 120 pieces on Palneg 2 with lot A and 60 pieces on Palneg 3
|
||||
-
|
||||
@@ -79,7 +78,6 @@
|
||||
-
|
||||
!python {model: stock.picking}: |
|
||||
stock_pack = self.pool.get('stock.pack.operation')
|
||||
self.do_prepare_partial(cr, uid, [ref('delivery_order_neg')], context=context)
|
||||
delivery_id = self.browse(cr, uid, ref('delivery_order_neg'), context=context)
|
||||
for rec in delivery_id.pack_operation_ids:
|
||||
if rec.package_id.name == 'Palneg 2':
|
||||
@@ -130,8 +128,6 @@
|
||||
-
|
||||
!python {model: stock.picking}: |
|
||||
self.action_confirm(cr, uid, [ref("delivery_reconcile")])
|
||||
self.action_confirm(cr, uid, [ref("delivery_reconcile")])
|
||||
self.do_prepare_partial(cr, uid, [ref("delivery_reconcile")])
|
||||
pick = self.browse(cr, uid, ref("delivery_reconcile"))
|
||||
ops_obj = self.pool.get("stock.pack.operation")
|
||||
pack_obj = self.pool.get("stock.quant.package")
|
||||
@@ -148,4 +144,3 @@
|
||||
pick = self.pool.get('stock.picking').browse(cr, uid, ref('delivery_reconcile'))
|
||||
customer_quant = self.search(cr, uid, [('product_id', '=', ref('product_neg')), ('location_id', '=', ref('stock_location_customers')), ('lot_id.name','=', 'Lot neg'), ('qty','=', 20)])
|
||||
assert pick.move_lines[0].id in [x.id for x in self.browse(cr, uid, customer_quant[0]).history_ids]
|
||||
|
||||
|
||||
@@ -14,11 +14,11 @@
|
||||
name: Delivery order for procurement
|
||||
partner_id: base.res_partner_2
|
||||
picking_type_id: stock.picking_type_out
|
||||
location_id: stock.stock_location_output
|
||||
location_dest_id: stock.stock_location_customers
|
||||
move_lines:
|
||||
- product_id: product.product_product_3
|
||||
product_uom_qty: 10.00
|
||||
location_id: stock.stock_location_output
|
||||
location_dest_id: stock.stock_location_customers
|
||||
procure_method: make_to_order
|
||||
-
|
||||
Confirm delivery order.
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
-
|
||||
Create an incoming picking for this product of 10 PCE from suppliers to stock
|
||||
-
|
||||
!record {model: stock.picking, id: pick1}:
|
||||
!record {model: stock.picking, id: pick1_wise}:
|
||||
name: Incoming picking
|
||||
partner_id: base.res_partner_2
|
||||
picking_type_id: picking_type_in
|
||||
@@ -23,23 +23,23 @@
|
||||
Confirm and assign picking and prepare partial
|
||||
-
|
||||
!python {model: stock.picking}: |
|
||||
self.action_confirm(cr, uid, [ref('pick1')], context=context)
|
||||
self.do_prepare_partial(cr, uid, [ref('pick1')], context=context)
|
||||
self.action_confirm(cr, uid, [ref('pick1_wise')], context=context)
|
||||
self.do_prepare_partial(cr, uid, [ref('pick1_wise')], context=context)
|
||||
-
|
||||
Put 6 pieces in shelf1 and 4 pieces in shelf2
|
||||
-
|
||||
!python {model: stock.picking}: |
|
||||
record = self.browse(cr, uid, ref('pick1'), context=context)
|
||||
record = self.browse(cr, uid, ref('pick1_wise'), context=context)
|
||||
stock_pack = self.pool.get('stock.pack.operation')
|
||||
stock_quant_pack = self.pool.get('stock.quant.package')
|
||||
package1 = stock_quant_pack.create(cr, uid, {'name': 'Pack 1'}, context=context)
|
||||
stock_pack.write(cr, uid, record.pack_operation_ids[0].id, {'result_package_id': package1, 'product_qty': 4, 'location_dest_id': ref('stock_location_components')})
|
||||
new_pack1 = stock_pack.create(cr, uid, {'product_id': ref('product_wise'), 'product_uom_id': ref('product.product_uom_unit'), 'picking_id': ref('pick1'), 'product_qty': 6.0, 'location_id': ref('stock_location_suppliers'), 'location_dest_id': ref('stock_location_14')}, context=context)
|
||||
new_pack1 = stock_pack.create(cr, uid, {'product_id': ref('product_wise'), 'product_uom_id': ref('product.product_uom_unit'), 'picking_id': ref('pick1_wise'), 'product_qty': 6.0, 'location_id': ref('stock_location_suppliers'), 'location_dest_id': ref('stock_location_14')}, context=context)
|
||||
-
|
||||
Transfer the receipt
|
||||
-
|
||||
!python {model: stock.picking}: |
|
||||
self.do_transfer(cr, uid, [ref('pick1')], context=context)
|
||||
self.do_transfer(cr, uid, [ref('pick1_wise')], context=context)
|
||||
-
|
||||
Check the system created 2 quants
|
||||
-
|
||||
|
||||
@@ -26,7 +26,9 @@ class TestVirtualAvailable(TestStockCommon):
|
||||
})
|
||||
|
||||
self.picking_out = self.env['stock.picking'].create({
|
||||
'picking_type_id': self.ref('stock.picking_type_out')})
|
||||
'picking_type_id': self.ref('stock.picking_type_out'),
|
||||
'location_id': self.stock_location,
|
||||
'location_dest_id': self.customer_location})
|
||||
self.env['stock.move'].create({
|
||||
'name': 'a move',
|
||||
'product_id': self.productA.id,
|
||||
@@ -34,10 +36,13 @@ class TestVirtualAvailable(TestStockCommon):
|
||||
'product_uom': self.productA.uom_id.id,
|
||||
'picking_id': self.picking_out.id,
|
||||
'location_id': self.stock_location,
|
||||
'location_dest_id': self.customer_location})
|
||||
'location_dest_id': self.customer_location
|
||||
})
|
||||
|
||||
self.picking_out_2 = self.env['stock.picking'].create({
|
||||
'picking_type_id': self.ref('stock.picking_type_out')})
|
||||
'picking_type_id': self.ref('stock.picking_type_out'),
|
||||
'location_id': self.stock_location,
|
||||
'location_dest_id': self.customer_location})
|
||||
self.env['stock.move'].create({
|
||||
'restrict_partner_id': self.ref('base.res_partner_4'),
|
||||
'name': 'another move',
|
||||
@@ -46,7 +51,8 @@ class TestVirtualAvailable(TestStockCommon):
|
||||
'product_uom': self.productA.uom_id.id,
|
||||
'picking_id': self.picking_out_2.id,
|
||||
'location_id': self.stock_location,
|
||||
'location_dest_id': self.customer_location})
|
||||
'location_dest_id': self.customer_location
|
||||
})
|
||||
|
||||
|
||||
def test_without_owner(self):
|
||||
|
||||
@@ -20,7 +20,9 @@ class TestStockFlow(TestStockCommon):
|
||||
|
||||
picking_in = self.PickingObj.create({
|
||||
'partner_id': self.partner_delta_id,
|
||||
'picking_type_id': self.picking_type_in})
|
||||
'picking_type_id': self.picking_type_in,
|
||||
'location_id': self.supplier_location,
|
||||
'location_dest_id': self.stock_location})
|
||||
self.MoveObj.create({
|
||||
'name': self.productA.name,
|
||||
'product_id': self.productA.id,
|
||||
@@ -161,7 +163,9 @@ class TestStockFlow(TestStockCommon):
|
||||
|
||||
picking_out = self.PickingObj.create({
|
||||
'partner_id': self.partner_agrolite_id,
|
||||
'picking_type_id': self.picking_type_out})
|
||||
'picking_type_id': self.picking_type_out,
|
||||
'location_id': self.stock_location,
|
||||
'location_dest_id': self.customer_location})
|
||||
self.MoveObj.create({
|
||||
'name': self.productA.name,
|
||||
'product_id': self.productA.id,
|
||||
@@ -432,7 +436,9 @@ class TestStockFlow(TestStockCommon):
|
||||
|
||||
picking_in_A = self.PickingObj.create({
|
||||
'partner_id': self.partner_delta_id,
|
||||
'picking_type_id': self.picking_type_in})
|
||||
'picking_type_id': self.picking_type_in,
|
||||
'location_id': self.supplier_location,
|
||||
'location_dest_id': self.stock_location})
|
||||
self.MoveObj.create({
|
||||
'name': self.DozA.name,
|
||||
'product_id': self.DozA.id,
|
||||
@@ -526,7 +532,9 @@ class TestStockFlow(TestStockCommon):
|
||||
|
||||
picking_in_B = self.PickingObj.create({
|
||||
'partner_id': self.partner_delta_id,
|
||||
'picking_type_id': self.picking_type_in})
|
||||
'picking_type_id': self.picking_type_in,
|
||||
'location_id': self.supplier_location,
|
||||
'location_dest_id': self.stock_location})
|
||||
self.MoveObj.create({
|
||||
'name': self.DozA.name,
|
||||
'product_id': self.DozA.id,
|
||||
@@ -711,7 +719,9 @@ class TestStockFlow(TestStockCommon):
|
||||
before_out_quantity = self.kgB.qty_available
|
||||
picking_out = self.PickingObj.create({
|
||||
'partner_id': self.partner_agrolite_id,
|
||||
'picking_type_id': self.picking_type_out})
|
||||
'picking_type_id': self.picking_type_out,
|
||||
'location_id': self.stock_location,
|
||||
'location_dest_id': self.customer_location})
|
||||
self.MoveObj.create({
|
||||
'name': self.kgB.name,
|
||||
'product_id': self.kgB.id,
|
||||
@@ -749,7 +759,9 @@ class TestStockFlow(TestStockCommon):
|
||||
|
||||
picking_out = self.PickingObj.create({
|
||||
'partner_id': self.partner_agrolite_id,
|
||||
'picking_type_id': self.picking_type_out})
|
||||
'picking_type_id': self.picking_type_out,
|
||||
'location_id': self.stock_location,
|
||||
'location_dest_id': self.customer_location})
|
||||
self.MoveObj.create({
|
||||
'name': self.DozA.name,
|
||||
'product_id': self.DozA.id,
|
||||
@@ -867,7 +879,9 @@ class TestStockFlow(TestStockCommon):
|
||||
productKG = self.ProductObj.create({'name': 'Product KG', 'uom_id': self.uom_kg.id, 'uom_po_id': self.uom_kg.id})
|
||||
picking_in = self.PickingObj.create({
|
||||
'partner_id': self.partner_delta_id,
|
||||
'picking_type_id': self.picking_type_in})
|
||||
'picking_type_id': self.picking_type_in,
|
||||
'location_id': self.supplier_location,
|
||||
'location_dest_id': self.stock_location})
|
||||
self.MoveObj.create({
|
||||
'name': productKG.name,
|
||||
'product_id': productKG.id,
|
||||
@@ -912,7 +926,9 @@ class TestStockFlow(TestStockCommon):
|
||||
self.assertEqual(productKG.qty_available, 1000, 'Wrong quantity available of product (%s found instead of 1000)' % (productKG.qty_available))
|
||||
picking_out = self.PickingObj.create({
|
||||
'partner_id': self.partner_agrolite_id,
|
||||
'picking_type_id': self.picking_type_out})
|
||||
'picking_type_id': self.picking_type_out,
|
||||
'location_id': self.stock_location,
|
||||
'location_dest_id': self.customer_location})
|
||||
self.MoveObj.create({
|
||||
'name': productKG.name,
|
||||
'product_id': productKG.id,
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
|
||||
<template id="assets_backend" name="stock assets" inherit_id="web.assets_backend">
|
||||
<xpath expr="." position="inside">
|
||||
<link rel="stylesheet" href="/stock/static/src/css/stock.css"/>
|
||||
<link rel="stylesheet" href="/stock/static/src/less/stock_dashboard.less"/>
|
||||
<script type="text/javascript" src="/stock/static/src/js/widgets.js"></script>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="barcode_index" name="Barcode Scanner"><!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Barcode Scanner</title>
|
||||
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
|
||||
<meta http-equiv="content-type" content="text/html, charset=utf-8" />
|
||||
|
||||
<meta name="viewport" content=" width=1024, user-scalable=no"/>
|
||||
<meta name="apple-mobile-web-app-capable" content="yes"/>
|
||||
<meta name="mobile-web-app-capable" content="yes"/>
|
||||
|
||||
<link rel="shortcut icon" sizes="80x51" href="/stock/static/src/img/scan.png"/>
|
||||
<link rel="shortcut icon" href="/web/static/src/img/favicon.ico" type="image/x-icon"/>
|
||||
|
||||
<link rel="stylesheet" href="/stock/static/src/css/barcode.css" />
|
||||
<link rel="stylesheet" href="/web/static/lib/bootstrap/css/bootstrap.css" />
|
||||
<link rel="stylesheet" href="/web/static/lib/jquery.ui/jquery-ui.css" />
|
||||
<link rel="stylesheet" href="/web/static/lib/fontawesome/css/font-awesome.css" />
|
||||
|
||||
<t t-call-assets="web.assets_common" t-css="false" />
|
||||
<t t-call-assets="web.assets_backend" t-css="false" />
|
||||
|
||||
<script type="text/javascript" id="loading-script" t-raw="init">
|
||||
odoo.define('web.web_client', function (require) {
|
||||
var WebClient = require('web.WebClient');
|
||||
var web_client = new WebClient();
|
||||
web_client.do_push_state = function (){};
|
||||
$(function () {
|
||||
web_client.setElement($(document.body));
|
||||
web_client.show_application = function () {
|
||||
web_client.action_manager.do_action("stock.ui", {});
|
||||
};
|
||||
web_client.start();
|
||||
});
|
||||
return web_client;
|
||||
});
|
||||
odoo.init();
|
||||
|
||||
</script>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<div class='openerp openerp_webclient_container'>
|
||||
<table class='oe_webclient'>
|
||||
<tr>
|
||||
<td class='oe_application' />
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
</template>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
@@ -6,4 +6,6 @@ import stock_return_picking
|
||||
import stock_change_product_qty
|
||||
import make_procurement_product
|
||||
import orderpoint_procurement
|
||||
import stock_transfer_details
|
||||
import stock_pack_details
|
||||
import stock_immediate_transfer
|
||||
import stock_backorder_confirmation
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from openerp import models, fields, api
|
||||
|
||||
class stock_backorder_confirmation(models.TransientModel):
|
||||
_name = 'stock.backorder.confirmation'
|
||||
_description = 'Backorder Confirmation'
|
||||
|
||||
pick_id = fields.Many2one('stock.picking')
|
||||
|
||||
@api.model
|
||||
def default_get(self, fields):
|
||||
res = {}
|
||||
active_id = self._context.get('active_id')
|
||||
if active_id:
|
||||
res = {'pick_id': active_id}
|
||||
return res
|
||||
|
||||
@api.multi
|
||||
def _process(self, cancel_backorder=False):
|
||||
self.ensure_one()
|
||||
for pack in self.pick_id.pack_operation_ids:
|
||||
if pack.qty_done > 0:
|
||||
pack.product_qty = pack.qty_done
|
||||
else:
|
||||
pack.unlink()
|
||||
self.pick_id.do_transfer()
|
||||
if cancel_backorder:
|
||||
backorder_pick = self.env['stock.picking'].search([('backorder_id', '=', self.pick_id.id)])
|
||||
backorder_pick.action_cancel()
|
||||
|
||||
@api.multi
|
||||
def process(self):
|
||||
self.ensure_one()
|
||||
self._process()
|
||||
|
||||
@api.multi
|
||||
def process_cancel_backorder(self):
|
||||
self.ensure_one()
|
||||
self._process(cancel_backorder=True)
|
||||
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<odoo>
|
||||
<record id="view_backorder_confirmation" model="ir.ui.view">
|
||||
<field name="name">stock_backorder_confirmation</field>
|
||||
<field name="model">stock.backorder.confirmation</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Backorder creation">
|
||||
<group>
|
||||
Do we need to create a backorder?
|
||||
</group>
|
||||
<footer>
|
||||
<button name="process" string="Create Backorder" type="object" class="oe_highlight"/>
|
||||
<button name="process_cancel_backorder" string="No Backorder" type="object" class="oe_highlight"/>
|
||||
or
|
||||
<button string="_Cancel" class="oe_link" special="cancel" />
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -0,0 +1,33 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from openerp import models, fields, api
|
||||
|
||||
class stock_immediate_transfer(models.TransientModel):
|
||||
_name = 'stock.immediate.transfer'
|
||||
_description = 'Immediate Transfer'
|
||||
|
||||
pick_id = fields.Many2one('stock.picking')
|
||||
|
||||
@api.model
|
||||
def default_get(self, fields):
|
||||
res = {}
|
||||
active_id = self._context.get('active_id')
|
||||
if active_id:
|
||||
res = {'pick_id': active_id}
|
||||
return res
|
||||
|
||||
@api.multi
|
||||
def process(self):
|
||||
self.ensure_one()
|
||||
# If still in draft => confirm and assign
|
||||
if self.pick_id.state == 'draft':
|
||||
self.pick_id.action_confirm()
|
||||
if self.pick_id.state != 'assigned':
|
||||
self.pick_id.action_assign()
|
||||
for pack in self.pick_id.pack_operation_ids:
|
||||
if pack.product_qty > 0:
|
||||
pack.write({'qty_done': pack.product_qty})
|
||||
else:
|
||||
pack.unlink()
|
||||
self.pick_id.do_transfer()
|
||||
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<odoo>
|
||||
<record id="view_immediate_transfer" model="ir.ui.view">
|
||||
<field name="name">view_immediate_transfer</field>
|
||||
<field name="model">stock.immediate.transfer</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Everything at once?">
|
||||
<group>
|
||||
<p>
|
||||
You did not enter any quantities. Click apply if you want to process everything at once.
|
||||
</p>
|
||||
</group>
|
||||
<footer>
|
||||
<button name="process" string="_Apply" type="object" class="oe_highlight"/>
|
||||
or
|
||||
<button string="_Cancel" class="oe_link" special="cancel" />
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -0,0 +1,72 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from openerp import models, fields, api
|
||||
import openerp.addons.decimal_precision as dp
|
||||
|
||||
class stock_pack_details(models.TransientModel):
|
||||
_name = 'stock.pack.details'
|
||||
_description = 'Pack details'
|
||||
|
||||
pack_id = fields.Many2one('stock.pack.operation', 'Pack operation')
|
||||
product_id = fields.Many2one('product.product', 'Product')
|
||||
product_uom_id = fields.Many2one('product.uom', 'Product Unit of Measure')
|
||||
qty_done = fields.Float('Processed Qty', digits=dp.get_precision('Product Unit of Measure'))
|
||||
quantity = fields.Float('Quantity', digits=dp.get_precision('Product Unit of Measure'), readonly=True)
|
||||
package_id = fields.Many2one('stock.quant.package', 'Source package', domain="['|', ('location_id', 'child_of', location_id), ('location_id','=',False)]")
|
||||
lot_id = fields.Many2one('stock.production.lot', 'Lot/Serial Number')
|
||||
location_id = fields.Many2one('stock.location', 'Source Location', required=True)
|
||||
location_dest_id = fields.Many2one('stock.location', 'Destination Location', required=True)
|
||||
result_package_id = fields.Many2one('stock.quant.package', 'Destination package', domain="['|', ('location_id', 'child_of', location_dest_id), ('location_id','=',False)]")
|
||||
picking_source_location_id = fields.Many2one('stock.location', related='pack_id.picking_id.location_id', readonly=True)
|
||||
picking_destination_location_id = fields.Many2one('stock.location', related='pack_id.picking_id.location_dest_id', readonly=True)
|
||||
|
||||
@api.model
|
||||
def default_get(self, fields):
|
||||
res = {}
|
||||
active_id = self._context.get('active_id')
|
||||
if active_id:
|
||||
pack_op = self.env['stock.pack.operation'].browse(active_id)
|
||||
res = {
|
||||
'pack_id': pack_op.id,
|
||||
'product_id': pack_op.product_id.id,
|
||||
'product_uom_id': pack_op.product_uom_id.id,
|
||||
'quantity': pack_op.product_qty,
|
||||
'qty_done': pack_op.qty_done,
|
||||
'package_id': pack_op.package_id.id,
|
||||
'lot_id': pack_op.lot_id.id,
|
||||
'location_id': pack_op.location_id.id,
|
||||
'location_dest_id': pack_op.location_dest_id.id,
|
||||
'result_package_id': pack_op.result_package_id.id,
|
||||
}
|
||||
return res
|
||||
|
||||
@api.multi
|
||||
def split_quantities(self):
|
||||
for wiz in self:
|
||||
if wiz.quantity>1 and wiz.qty_done < wiz.quantity:
|
||||
pack2 = self.pack_id.copy({'qty_done': 0.0, 'product_qty': wiz.quantity - wiz.qty_done})
|
||||
wiz.quantity = wiz.qty_done
|
||||
self.pack_id.write({'qty_done': wiz.qty_done, 'product_qty': wiz.quantity})
|
||||
return {
|
||||
'view_type': 'form',
|
||||
'view_mode': 'form',
|
||||
'res_model': 'stock.picking',
|
||||
'type': 'ir.actions.act_window',
|
||||
'res_id': self.pack_id.picking_id.id,
|
||||
}
|
||||
|
||||
@api.one
|
||||
def process(self):
|
||||
pack = self.pack_id
|
||||
pack.write({
|
||||
'product_id': self.product_id.id,
|
||||
'product_uom_id': self.product_uom_id.id,
|
||||
'qty_done': self.qty_done,
|
||||
'package_id': self.package_id.id,
|
||||
'lot_id': self.lot_id.id,
|
||||
'location_id': self.location_id.id,
|
||||
'location_dest_id': self.location_dest_id.id,
|
||||
'result_package_id': self.result_package_id.id,
|
||||
})
|
||||
return {}
|
||||
@@ -0,0 +1,57 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<odoo>
|
||||
<record id="view_pack_details" model="ir.ui.view">
|
||||
<field name="name">Enter pack details</field>
|
||||
<field name="model">stock.pack.details</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Enter Pack Details">
|
||||
<group>
|
||||
<group string="#Products">
|
||||
<field name="pack_id" invisible="1"/>
|
||||
<field name="picking_source_location_id" invisible="True"/>
|
||||
<field name="picking_destination_location_id" invisible="True"/>
|
||||
|
||||
<field name="product_id" attrs="{'required': [('package_id', '=', False)]}"/>
|
||||
<field name="lot_id" groups="stock.group_production_lot" domain="[('product_id','=?', product_id)]" context="{'product_id': product_id}"/>
|
||||
</group>
|
||||
<group string="From">
|
||||
<field name="location_id" domain="[('id', 'child_of', picking_source_location_id)]"/>
|
||||
<field name="package_id" attrs="{'required': [('product_id', '=', False)]}" groups="stock.group_tracking_lot" context="{'location_id': location_id}"/>
|
||||
</group>
|
||||
</group>
|
||||
<group>
|
||||
<group string="Actions">
|
||||
<label for="qty_done" string="Done"/>
|
||||
<div class="oe_inline">
|
||||
<field name="qty_done"/>
|
||||
OF
|
||||
<field name="quantity"/>
|
||||
<field name="product_uom_id" options="{"no_open": True}" groups="product.group_uom"/>
|
||||
<button name="split_quantities" string="Split" type="object" icon="STOCK_PREFERENCES" attrs="{'invisible': ['|', ('quantity', '=', 1), ('qty_done', '=', 0.0)]}"/>
|
||||
</div>
|
||||
</group>
|
||||
<group string="To">
|
||||
<field name="location_dest_id" domain="[('id', 'child_of', picking_destination_location_id)]"/>
|
||||
<field name="result_package_id" groups="stock.group_tracking_lot" context="{'location_id': location_dest_id}"/>
|
||||
</group>
|
||||
</group>
|
||||
<footer>
|
||||
<button name="process" string="_Apply" type="object" class="oe_highlight"/>
|
||||
or
|
||||
<button string="_Cancel" class="oe_link" special="cancel" />
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="pack_details" model="ir.actions.act_window">
|
||||
<field name="name">Pack Details</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">stock.pack.details</field>
|
||||
<field name="view_id" ref="view_pack_details"/>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="target">new</field>
|
||||
<field name="context">{}</field>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -107,6 +107,8 @@ class stock_return_picking(osv.osv_memory):
|
||||
'picking_type_id': pick_type_id,
|
||||
'state': 'draft',
|
||||
'origin': pick.name,
|
||||
'location_id': pick.location_dest_id.id,
|
||||
'location_dest_id': pick.location_id.id,
|
||||
}, context=context)
|
||||
|
||||
for data_get in data_obj.browse(cr, uid, data['product_return_moves'], context=context):
|
||||
@@ -139,8 +141,6 @@ class stock_return_picking(osv.osv_memory):
|
||||
if not returned_lines:
|
||||
raise UserError(_("Please specify at least one non-zero quantity."))
|
||||
|
||||
pick_obj.action_confirm(cr, uid, [new_picking], context=context)
|
||||
pick_obj.action_assign(cr, uid, [new_picking], context)
|
||||
return new_picking, pick_type_id
|
||||
|
||||
def create_returns(self, cr, uid, ids, context=None):
|
||||
|
||||
@@ -1,164 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from openerp import models, fields, api
|
||||
from openerp.tools.translate import _
|
||||
import openerp.addons.decimal_precision as dp
|
||||
from datetime import datetime
|
||||
|
||||
class stock_transfer_details(models.TransientModel):
|
||||
_name = 'stock.transfer_details'
|
||||
_description = 'Picking wizard'
|
||||
|
||||
picking_id = fields.Many2one('stock.picking', 'Picking')
|
||||
item_ids = fields.One2many('stock.transfer_details_items', 'transfer_id', 'Items', domain=[('product_id', '!=', False)])
|
||||
packop_ids = fields.One2many('stock.transfer_details_items', 'transfer_id', 'Packs', domain=[('product_id', '=', False)])
|
||||
picking_source_location_id = fields.Many2one('stock.location', string="Head source location", related='picking_id.location_id', store=False, readonly=True)
|
||||
picking_destination_location_id = fields.Many2one('stock.location', string="Head destination location", related='picking_id.location_dest_id', store=False, readonly=True)
|
||||
|
||||
def default_get(self, cr, uid, fields, context=None):
|
||||
if context is None: context = {}
|
||||
res = super(stock_transfer_details, self).default_get(cr, uid, fields, context=context)
|
||||
picking_ids = context.get('active_ids', [])
|
||||
active_model = context.get('active_model')
|
||||
|
||||
if not picking_ids or len(picking_ids) != 1:
|
||||
# Partial Picking Processing may only be done for one picking at a time
|
||||
return res
|
||||
assert active_model in ('stock.picking'), 'Bad context propagation'
|
||||
picking_id, = picking_ids
|
||||
picking = self.pool.get('stock.picking').browse(cr, uid, picking_id, context=context)
|
||||
items = []
|
||||
packs = []
|
||||
if not picking.pack_operation_ids:
|
||||
picking.do_prepare_partial()
|
||||
for op in picking.pack_operation_ids:
|
||||
item = {
|
||||
'packop_id': op.id,
|
||||
'product_id': op.product_id.id,
|
||||
'product_uom_id': op.product_uom_id.id,
|
||||
'quantity': op.product_qty,
|
||||
'package_id': op.package_id.id,
|
||||
'lot_id': op.lot_id.id,
|
||||
'sourceloc_id': op.location_id.id,
|
||||
'destinationloc_id': op.location_dest_id.id,
|
||||
'result_package_id': op.result_package_id.id,
|
||||
'date': op.date,
|
||||
'owner_id': op.owner_id.id,
|
||||
}
|
||||
if op.product_id:
|
||||
items.append(item)
|
||||
elif op.package_id:
|
||||
packs.append(item)
|
||||
res.update(item_ids=items)
|
||||
res.update(packop_ids=packs)
|
||||
return res
|
||||
|
||||
@api.one
|
||||
def do_detailed_transfer(self):
|
||||
processed_ids = []
|
||||
# Create new and update existing pack operations
|
||||
for lstits in [self.item_ids, self.packop_ids]:
|
||||
for prod in lstits:
|
||||
pack_datas = {
|
||||
'product_id': prod.product_id.id,
|
||||
'product_uom_id': prod.product_uom_id.id,
|
||||
'product_qty': prod.quantity,
|
||||
'package_id': prod.package_id.id,
|
||||
'lot_id': prod.lot_id.id,
|
||||
'location_id': prod.sourceloc_id.id,
|
||||
'location_dest_id': prod.destinationloc_id.id,
|
||||
'result_package_id': prod.result_package_id.id,
|
||||
'date': prod.date if prod.date else datetime.now(),
|
||||
'owner_id': prod.owner_id.id,
|
||||
}
|
||||
if prod.packop_id:
|
||||
prod.packop_id.with_context(no_recompute=True).write(pack_datas)
|
||||
processed_ids.append(prod.packop_id.id)
|
||||
else:
|
||||
pack_datas['picking_id'] = self.picking_id.id
|
||||
packop_id = self.env['stock.pack.operation'].create(pack_datas)
|
||||
processed_ids.append(packop_id.id)
|
||||
# Delete the others
|
||||
packops = self.env['stock.pack.operation'].search(['&', ('picking_id', '=', self.picking_id.id), '!', ('id', 'in', processed_ids)])
|
||||
packops.unlink()
|
||||
|
||||
# Execute the transfer of the picking
|
||||
self.picking_id.do_transfer()
|
||||
|
||||
return True
|
||||
|
||||
@api.multi
|
||||
def wizard_view(self):
|
||||
view = self.env.ref('stock.view_stock_enter_transfer_details')
|
||||
|
||||
return {
|
||||
'name': _('Enter transfer details'),
|
||||
'type': 'ir.actions.act_window',
|
||||
'view_type': 'form',
|
||||
'view_mode': 'form',
|
||||
'res_model': 'stock.transfer_details',
|
||||
'views': [(view.id, 'form')],
|
||||
'view_id': view.id,
|
||||
'target': 'new',
|
||||
'res_id': self.ids[0],
|
||||
'context': self.env.context,
|
||||
}
|
||||
|
||||
|
||||
class stock_transfer_details_items(models.TransientModel):
|
||||
_name = 'stock.transfer_details_items'
|
||||
_description = 'Picking wizard items'
|
||||
|
||||
transfer_id = fields.Many2one('stock.transfer_details', 'Transfer')
|
||||
packop_id = fields.Many2one('stock.pack.operation', 'Operation')
|
||||
product_id = fields.Many2one('product.product', 'Product')
|
||||
product_uom_id = fields.Many2one('product.uom', 'Product Unit of Measure')
|
||||
quantity = fields.Float('Quantity', digits=dp.get_precision('Product Unit of Measure'), default = 1.0)
|
||||
package_id = fields.Many2one('stock.quant.package', 'Source package', domain="['|', ('location_id', 'child_of', sourceloc_id), ('location_id','=',False)]")
|
||||
lot_id = fields.Many2one('stock.production.lot', 'Lot/Serial Number')
|
||||
sourceloc_id = fields.Many2one('stock.location', 'Source Location', required=True)
|
||||
destinationloc_id = fields.Many2one('stock.location', 'Destination Location', required=True)
|
||||
result_package_id = fields.Many2one('stock.quant.package', 'Destination package', domain="['|', ('location_id', 'child_of', destinationloc_id), ('location_id','=',False)]")
|
||||
date = fields.Datetime('Date')
|
||||
owner_id = fields.Many2one('res.partner', 'Owner', help="Owner of the quants")
|
||||
|
||||
|
||||
|
||||
@api.multi
|
||||
def split_quantities(self):
|
||||
for det in self:
|
||||
if det.quantity>1:
|
||||
det.quantity = (det.quantity-1)
|
||||
new_id = det.copy(context=self.env.context)
|
||||
new_id.quantity = 1
|
||||
new_id.packop_id = False
|
||||
if self and self[0]:
|
||||
return self[0].transfer_id.wizard_view()
|
||||
|
||||
@api.multi
|
||||
def put_in_pack(self):
|
||||
newpack = None
|
||||
for packop in self:
|
||||
if not packop.result_package_id:
|
||||
if not newpack:
|
||||
newpack = self.pool['stock.quant.package'].create(self._cr, self._uid, {'location_id': packop.destinationloc_id.id if packop.destinationloc_id else False}, self._context)
|
||||
packop.result_package_id = newpack
|
||||
if self and self[0]:
|
||||
return self[0].transfer_id.wizard_view()
|
||||
|
||||
@api.multi
|
||||
def product_id_change(self, product, uom=False):
|
||||
result = {}
|
||||
if product:
|
||||
prod = self.env['product.product'].browse(product)
|
||||
result['product_uom_id'] = prod.uom_id and prod.uom_id.id
|
||||
return {'value': result, 'domain': {}, 'warning':{} }
|
||||
|
||||
@api.multi
|
||||
def source_package_change(self, sourcepackage):
|
||||
result = {}
|
||||
if sourcepackage:
|
||||
pack = self.env['stock.quant.package'].browse(sourcepackage)
|
||||
result['sourceloc_id'] = pack.location_id and pack.location_id.id
|
||||
return {'value': result, 'domain': {}, 'warning':{} }
|
||||
@@ -1,61 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
<record id="view_stock_enter_transfer_details" model="ir.ui.view">
|
||||
<field name="name">Enter transfer details</field>
|
||||
<field name="model">stock.transfer_details</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Transfer details" version="7">
|
||||
<field name="picking_source_location_id" invisible="True"/>
|
||||
<field name="picking_destination_location_id" invisible="True"/>
|
||||
<div class="oe_grey" groups="stock.group_tracking_lot">
|
||||
Setting a product and a source package means that the product will be taken
|
||||
out of the package.
|
||||
</div>
|
||||
<group>
|
||||
<field name="item_ids"
|
||||
context="{'default_sourceloc_id':picking_source_location_id,
|
||||
'default_destinationloc_id':picking_destination_location_id}" nolabel="1">
|
||||
<tree string="Inventory Details" editable="bottom" >
|
||||
<field name="package_id" groups="stock.group_tracking_lot"/>
|
||||
<field name="product_id" required="True" context="{'uom':product_uom_id}" on_change="product_id_change(product_id,product_uom_id,context)"/>
|
||||
<field name="quantity"/>
|
||||
<button name="split_quantities" string="Split" type="object" icon="STOCK_PREFERENCES" attrs="{'invisible': [('quantity', '=', 1)]}"/>
|
||||
<field name="product_uom_id" options="{"no_open": True}" groups="product.group_uom"/>
|
||||
<field name="sourceloc_id" domain="[('id', 'child_of', parent.picking_source_location_id)]"/>
|
||||
<field name="destinationloc_id" domain="[('id', 'child_of', parent.picking_destination_location_id)]"/>
|
||||
<field name="result_package_id" groups="stock.group_tracking_lot" context="{'location_id': destinationloc_id}"/>
|
||||
<button name="put_in_pack" string="Pack" type="object" icon="terp-product" attrs="{'invisible': [('result_package_id', '!=', False)]}" groups="stock.group_tracking_lot"/>
|
||||
<field name="lot_id" groups="stock.group_production_lot" domain="[('product_id','=?', product_id)]" context="{'product_id': product_id}"/>
|
||||
</tree>
|
||||
</field>
|
||||
</group>
|
||||
<group string="Packages To Move" groups="stock.group_tracking_lot">
|
||||
<div class="oe_grey">
|
||||
The source package will be moved entirely. If you specify a destination package, the source package will be put in the destination package.
|
||||
</div>
|
||||
</group>
|
||||
<group groups="stock.group_tracking_lot">
|
||||
<field name="packop_ids"
|
||||
context="{'default_sourceloc_id':picking_source_location_id,
|
||||
'default_destinationloc_id':picking_destination_location_id}" nolabel="1">
|
||||
<tree editable="bottom">
|
||||
<field name="package_id" required="True" on_change="source_package_change(package_id)"/>
|
||||
<field name="sourceloc_id" domain="[('id', 'child_of', parent.picking_source_location_id)]"/>
|
||||
<field name="destinationloc_id" domain="[('id', 'child_of', parent.picking_destination_location_id)]"/>
|
||||
<field name="result_package_id"/>
|
||||
<button name="put_in_pack" string="Pack" type="object" icon="terp-product" attrs="{'invisible': [('result_package_id', '!=', False)]}"/>
|
||||
</tree>
|
||||
</field>
|
||||
</group>
|
||||
<footer>
|
||||
<button name="do_detailed_transfer" string="_Apply" type="object" class="oe_highlight"/>
|
||||
or
|
||||
<button string="_Cancel" class="oe_link" special="cancel" />
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
@@ -58,7 +58,7 @@
|
||||
<button name="%(action_stock_invoice_onshipping)d" string="Create Invoice" attrs="{'invisible': ['|',('state','<>','done'),('invoice_state','<>','2binvoiced')]}" type="action" class="oe_highlight" groups="base.group_user"/>
|
||||
</xpath>
|
||||
<field name="move_lines" position="attributes">
|
||||
<attribute name="context">{'default_invoice_state': invoice_state, 'address_in_id': partner_id, 'form_view_ref':'stock.view_move_picking_form', 'tree_view_ref':'stock.view_move_picking_tree', 'default_picking_type_id': picking_type_id}</attribute>
|
||||
<attribute name="context">{'default_invoice_state': invoice_state, 'address_in_id': partner_id, 'form_view_ref':'stock.view_move_picking_form', 'tree_view_ref':'stock.view_move_picking_tree', 'default_picking_type_id': picking_type_id, 'default_location_id': location_id, 'default_location_dest_id': location_dest_id}</attribute>
|
||||
</field>
|
||||
<xpath expr="//field[@name='move_type']" position="after">
|
||||
<field name="invoice_state" groups="account.group_account_invoice"/>
|
||||
|
||||
@@ -2,3 +2,4 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import stock_invoice_directly
|
||||
import wizards
|
||||
@@ -9,15 +9,16 @@ from openerp.tools.translate import _
|
||||
class stock_picking(osv.osv):
|
||||
_inherit = 'stock.picking'
|
||||
|
||||
@api.cr_uid_ids_context
|
||||
def do_transfer(self, cr, uid, picking_ids, context=None):
|
||||
def do_new_transfer(self, cr, uid, ids, context=None):
|
||||
"""Launch Create invoice wizard if invoice state is To be Invoiced,
|
||||
after processing the picking.
|
||||
"""
|
||||
if context is None:
|
||||
context = {}
|
||||
res = super(stock_picking, self).do_transfer(cr, uid, picking_ids, context=context)
|
||||
pick_ids = [p.id for p in self.browse(cr, uid, picking_ids, context) if p.invoice_state == '2binvoiced']
|
||||
res = super(stock_picking, self).do_new_transfer(cr, uid, ids, context=context)
|
||||
if res: #If it is already returning a wizard
|
||||
return res
|
||||
pick_ids = [p.id for p in self.browse(cr, uid, ids, context) if p.invoice_state == '2binvoiced']
|
||||
if pick_ids:
|
||||
context = dict(context, active_model='stock.picking', active_ids=pick_ids)
|
||||
return {
|
||||
@@ -29,4 +30,4 @@ class stock_picking(osv.osv):
|
||||
'target': 'new',
|
||||
'context': context
|
||||
}
|
||||
return res
|
||||
return res
|
||||
@@ -0,0 +1,56 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from openerp import models, fields, api
|
||||
from openerp.tools.translate import _
|
||||
|
||||
class stock_immediate_transfer(models.TransientModel):
|
||||
_inherit = 'stock.immediate.transfer'
|
||||
|
||||
@api.multi
|
||||
def process(self):
|
||||
super(stock_immediate_transfer, self).process()
|
||||
pick = self.pick_id.id
|
||||
context = dict(self.env.context, active_model='stock.picking', active_id=pick, active_ids=[pick])
|
||||
print context
|
||||
return {
|
||||
'name': _('Create Invoice'),
|
||||
'view_type': 'form',
|
||||
'view_mode': 'form',
|
||||
'res_model': 'stock.invoice.onshipping',
|
||||
'type': 'ir.actions.act_window',
|
||||
'target': 'new',
|
||||
'context': context
|
||||
}
|
||||
|
||||
|
||||
class stock_backorder_confirmation(models.TransientModel):
|
||||
_inherit = 'stock.backorder.confirmation'
|
||||
|
||||
@api.multi
|
||||
def process(self):
|
||||
res = super(stock_backorder_confirmation, self).process()
|
||||
context = dict(self.env.context, active_model='stock.picking', active_id=self.pick_id.id, active_ids=[self.pick_id.id])
|
||||
return {
|
||||
'name': _('Create Invoice'),
|
||||
'view_type': 'form',
|
||||
'view_mode': 'form',
|
||||
'res_model': 'stock.invoice.onshipping',
|
||||
'type': 'ir.actions.act_window',
|
||||
'target': 'new',
|
||||
'context': context
|
||||
}
|
||||
|
||||
@api.multi
|
||||
def process_cancel_backorder(self):
|
||||
res = super(stock_backorder_confirmation, self).process_cancel_backorder()
|
||||
context = dict(self.env.context, active_model='stock.picking', active_id=self.pick_id.id, active_ids=[self.pick_id.id])
|
||||
return {
|
||||
'name': _('Create Invoice'),
|
||||
'view_type': 'form',
|
||||
'view_mode': 'form',
|
||||
'res_model': 'stock.invoice.onshipping',
|
||||
'type': 'ir.actions.act_window',
|
||||
'target': 'new',
|
||||
'context': context
|
||||
}
|
||||
@@ -24,14 +24,18 @@
|
||||
<field name="priority">2</field>
|
||||
<field name="picking_type_id" ref="stock.picking_type_internal"/>
|
||||
<field name="wave_id" ref="stock_picking_wave_freeze_1"/>
|
||||
<field name="location_id" ref="stock.stock_location_stock"/>
|
||||
<field name="location_dest_id" ref="stock.stock_location_output"/>
|
||||
</record>
|
||||
<record id="Picking_B" model="stock.picking">
|
||||
<field name="name">Picking #2</field>
|
||||
<field name="move_type">one</field>
|
||||
<field name="state">assigned</field>
|
||||
<field name="priority">1</field>
|
||||
<field name="picking_type_id" ref="stock.picking_type_internal"/>
|
||||
<field name="wave_id" ref="stock_picking_wave_dry_1"/>
|
||||
<field name="picking_type_id" ref="stock.picking_type_internal"/>
|
||||
<field name="wave_id" ref="stock_picking_wave_dry_1"/>
|
||||
<field name="location_id" ref="stock.stock_location_stock"/>
|
||||
<field name="location_dest_id" ref="stock.stock_location_output"/>
|
||||
</record>
|
||||
<record id="Picking_C" model="stock.picking">
|
||||
<field name="name">Picking #3</field>
|
||||
@@ -39,7 +43,9 @@
|
||||
<field name="state">assigned</field>
|
||||
<field name="priority">1</field>
|
||||
<field name="picking_type_id" ref="stock.picking_type_internal"/>
|
||||
<field name="wave_id" ref="stock_picking_wave_freeze_1"/>
|
||||
<field name="wave_id" ref="stock_picking_wave_freeze_1"/>
|
||||
<field name="location_id" ref="stock.stock_location_stock"/>
|
||||
<field name="location_dest_id" ref="stock.stock_location_output"/>
|
||||
</record>
|
||||
|
||||
<!-- add Product -->
|
||||
|
||||
Reference in New Issue
Block a user