[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:
Josse Colpaert
2015-06-15 10:18:17 +02:00
parent 0bfb7d6915
commit 2cb3a90c1c
38 changed files with 783 additions and 2158 deletions
@@ -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>
+1 -1
View File
@@ -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"/>
+11 -9
View File
@@ -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:
+3 -1
View File
@@ -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)
+5 -1
View File
@@ -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
-
+3 -4
View File
@@ -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'],
}
+14 -6
View File
@@ -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)
+2 -2
View File
@@ -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 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
65 access_procurement_rule_internal procurement.rule.flow internal model_procurement_rule base.group_user 1 0 0 0
66 access_stock_pack_operation_manager stock.pack.operation manager model_stock_pack_operation stock.group_stock_manager 1 1 1 1
67 access_stock_pack_operation_user stock.pack.operation user model_stock_pack_operation stock.group_stock_user 1 1 1 1
68 access_stock_pack_operation_all stock.pack.operation all users model_stock_pack_operation base.group_user 1 0 1 0 1 0 1
69 access_product_putaway_all product.putaway all users model_product_putaway base.group_user 1 0 0 0
70 access_product_putaway_manager product.putaway all managers model_product_putaway stock.group_stock_manager 1 1 1 1
71 access_stock_removal_all product.removal all users model_product_removal base.group_user 1 0 0 0
73 access_stock_fixed_putaway_user stock_fixed_putaway_strat user model_stock_fixed_putaway_strat stock.group_stock_user 1 0 0 0
74 access_stock_move_operation_link_manager stock.move.operation.link manager model_stock_move_operation_link stock.group_stock_manager 1 1 1 1
75 access_stock_move_operation_link_user stock.move.operation.link user model_stock_move_operation_link stock.group_stock_user 1 1 1 1
76 access_stock_move_operation_link_all stock.move.operation.link all users model_stock_move_operation_link base.group_user 1 0 1 0 1 0 1
77 access_product_price_history_stock_user prices.history stock user product.model_product_price_history stock.group_stock_user 1 0 1 0
78 access_product_price_history_stock_manager prices.history stock manager product.model_product_price_history stock.group_stock_manager 1 1 1 1
79 access_barcode_nomenclature_stock_user barcode.nomenclature.stock.user barcodes.model_barcode_nomenclature stock.group_stock_user 1 0 0 0
-134
View File
@@ -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;
}
-21
View File
@@ -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;
}
}
-274
View File
@@ -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 &amp;&amp; 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' &amp;&amp; !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 &#47; Change Lot</a></li>
</t>
<t t-if="!row.cols.head_container &amp;&amp; !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">&lt; Previous</button>
<button type="button" class="btn btn-default navbar-btn js_pick_next">Next &gt;</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
View File
@@ -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
View File
@@ -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
View File
@@ -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&gt;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="&#8627;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&gt;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','&lt;&gt;','view')]"/>
<field name="location_dest_id" domain="[('usage','&lt;&gt;','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"/>
-5
View File
@@ -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]
+2 -2
View File
@@ -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.
+6 -6
View File
@@ -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
-
+10 -4
View File
@@ -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):
+24 -8
View File
@@ -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,
-68
View File
@@ -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">&lt;!DOCTYPE html&gt;
<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>
+3 -1
View File
@@ -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>
+72
View File
@@ -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="{&quot;no_open&quot;: 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>
+2 -2
View File
@@ -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="{&quot;no_open&quot;: 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>
+1 -1
View File
@@ -58,7 +58,7 @@
<button name="%(action_stock_invoice_onshipping)d" string="Create Invoice" attrs="{'invisible': ['|',('state','&lt;&gt;','done'),('invoice_state','&lt;&gt;','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
+56
View File
@@ -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 -->