[MIG] stock_request_kanban: Migration to 13.0

This commit is contained in:
hveficent
2020-01-30 13:39:10 +01:00
committed by Bernat Puig Font
parent 918801ec94
commit 6c8a1894b1
26 changed files with 931 additions and 656 deletions

View File

@@ -2,30 +2,27 @@
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html). # License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
{ {
'name': 'Stock Request kanban', "name": "Stock Request kanban",
'version': '12.0.1.0.1', "version": "13.0.1.0.1",
'category': 'Reporting', "category": "Warehouse Management",
'website': 'https://github.com/OCA/stock-logistics-warehouse', "website": "https://github.com/OCA/stock-logistics-warehouse",
'author': 'Creu Blanca, Eficent, Odoo Community Association (OCA)', "author": "Creu Blanca, ForgeFlow, Odoo Community Association (OCA)",
'license': 'AGPL-3', "license": "LGPL-3",
'summary': 'Adds a stock request order, and takes stock requests as lines', "summary": "Adds a stock request order, and takes stock requests as lines",
'depends': [ "depends": ["stock_request", "barcodes"],
'stock_request', "data": [
'barcodes', "data/stock_request_sequence_data.xml",
"report/report_paper_format.xml",
"wizard/wizard_stock_inventory_kanban_views.xml",
"wizard/wizard_stock_request_kanban_views.xml",
"wizard/wizard_stock_request_order_kanban_views.xml",
"views/stock_request_order_views.xml",
"views/stock_request_kanban_views.xml",
"views/stock_inventory_kanban_views.xml",
"views/stock_request_menu.xml",
"report/stock_request_kanban_templates.xml",
"security/ir.model.access.csv",
], ],
'data': [ "installable": True,
'data/stock_request_sequence_data.xml', "application": False,
'report/report_paper_format.xml',
'wizard/wizard_stock_inventory_kanban_views.xml',
'wizard/wizard_stock_request_kanban_views.xml',
'wizard/wizard_stock_request_order_kanban_views.xml',
'views/stock_request_order_views.xml',
'views/stock_request_kanban_views.xml',
'views/stock_inventory_kanban_views.xml',
'views/stock_request_menu.xml',
'report/stock_request_kanban_templates.xml',
'security/ir.model.access.csv',
],
'installable': True,
'application': False,
} }

View File

@@ -1,22 +1,19 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8" ?>
<odoo> <odoo>
<data noupdate="1"> <data noupdate="1">
<record id="seq_stock_request_kanban" model="ir.sequence"> <record id="seq_stock_request_kanban" model="ir.sequence">
<field name="name">Stock Request Kanban</field> <field name="name">Stock Request Kanban</field>
<field name="code">stock.request.kanban</field> <field name="code">stock.request.kanban</field>
<field name="prefix">KB</field> <field name="prefix">KB</field>
<field name="padding">5</field> <field name="padding">5</field>
<field name="company_id" eval="False"/> <field name="company_id" eval="False" />
</record> </record>
<record id="seq_stock_inventory_kanban" model="ir.sequence"> <record id="seq_stock_inventory_kanban" model="ir.sequence">
<field name="name">Stock Inventory Kanban</field> <field name="name">Stock Inventory Kanban</field>
<field name="code">stock.inventory.kanban</field> <field name="code">stock.inventory.kanban</field>
<field name="prefix">IKB</field> <field name="prefix">IKB</field>
<field name="padding">5</field> <field name="padding">5</field>
<field name="company_id" eval="False"/> <field name="company_id" eval="False" />
</record> </record>
</data> </data>
</odoo> </odoo>

View File

@@ -1,69 +1,76 @@
# Copyright 2018 Creu Blanca # Copyright 2018 Creu Blanca
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html). # License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
from odoo import api, models, fields from odoo import api, fields, models
from odoo.osv import expression from odoo.osv import expression
class StockInventoryKanban(models.Model): class StockInventoryKanban(models.Model):
_name = 'stock.inventory.kanban' _name = "stock.inventory.kanban"
_description = 'Inventory for Kanban' _description = "Inventory for Kanban"
_inherit = ['mail.thread', 'mail.activity.mixin'] _inherit = ["mail.thread", "mail.activity.mixin"]
name = fields.Char( name = fields.Char(
readonly=True, states={'draft': [('readonly', False)]}, copy=False, readonly=True, states={"draft": [("readonly", False)]}, copy=False
) )
state = fields.Selection( state = fields.Selection(
[ [
('draft', 'Draft'), ("draft", "Draft"),
('in_progress', 'In progress'), ("in_progress", "In progress"),
('finished', 'Finished'), ("finished", "Finished"),
('closed', 'Closed'), ("closed", "Closed"),
('cancelled', 'Cancelled') ("cancelled", "Cancelled"),
], ],
required=True, default='draft', readonly=True, copy=False, required=True,
track_visibility='onchange' default="draft",
readonly=True,
copy=False,
track_visibility="onchange",
) )
warehouse_ids = fields.Many2many( warehouse_ids = fields.Many2many(
'stock.warehouse', string='Warehouse', "stock.warehouse",
string="Warehouse",
ondelete="cascade", ondelete="cascade",
readonly=True, states={'draft': [('readonly', False)]}, readonly=True,
states={"draft": [("readonly", False)]},
) )
location_ids = fields.Many2many( location_ids = fields.Many2many(
'stock.location', string='Location', "stock.location",
domain=[('usage', 'in', ['internal', 'transit'])], string="Location",
domain=[("usage", "in", ["internal", "transit"])],
ondelete="cascade", ondelete="cascade",
readonly=True, states={'draft': [('readonly', False)]}, readonly=True,
states={"draft": [("readonly", False)]},
) )
product_ids = fields.Many2many( product_ids = fields.Many2many(
'product.product', string='Products', "product.product",
domain=[('type', 'in', ['product', 'consu'])], string="Products",
ondelete='cascade', domain=[("type", "in", ["product", "consu"])],
readonly=True, states={'draft': [('readonly', False)]}, ondelete="cascade",
readonly=True,
states={"draft": [("readonly", False)]},
) )
kanban_ids = fields.Many2many( kanban_ids = fields.Many2many(
'stock.request.kanban', "stock.request.kanban",
relation='stock_inventory_kanban_kanban', relation="stock_inventory_kanban_kanban",
readonly=True, copy=False, readonly=True,
copy=False,
) )
scanned_kanban_ids = fields.Many2many( scanned_kanban_ids = fields.Many2many(
'stock.request.kanban', "stock.request.kanban",
relation='stock_inventory_kanban_scanned_kanban', relation="stock_inventory_kanban_scanned_kanban",
readonly=True, copy=False, readonly=True,
copy=False,
) )
missing_kanban_ids = fields.Many2many( missing_kanban_ids = fields.Many2many(
'stock.request.kanban', "stock.request.kanban", readonly=True, compute="_compute_missing_kanban"
readonly=True,
compute='_compute_missing_kanban'
) )
count_missing_kanbans = fields.Integer( count_missing_kanbans = fields.Integer(
'Missing Kanbans', "Missing Kanbans", readonly=True, compute="_compute_missing_kanban"
readonly=True,
compute='_compute_missing_kanban',
) )
@api.depends('kanban_ids', 'scanned_kanban_ids') @api.depends("kanban_ids", "scanned_kanban_ids")
def _compute_missing_kanban(self): def _compute_missing_kanban(self):
for rec in self: for rec in self:
rec.missing_kanban_ids = rec.kanban_ids.filtered( rec.missing_kanban_ids = rec.kanban_ids.filtered(
@@ -74,88 +81,73 @@ class StockInventoryKanban(models.Model):
def _get_inventory_kanban_domain(self): def _get_inventory_kanban_domain(self):
domain = [] domain = []
if self.warehouse_ids: if self.warehouse_ids:
domain = expression.AND(( domain = expression.AND(
domain, [('warehouse_id', 'in', self.warehouse_ids.ids)] (domain, [("warehouse_id", "in", self.warehouse_ids.ids)])
)) )
if self.product_ids: if self.product_ids:
domain = expression.AND(( domain = expression.AND(
domain, [('product_id', 'in', self.product_ids.ids)] (domain, [("product_id", "in", self.product_ids.ids)])
)) )
if self.location_ids: if self.location_ids:
domain = expression.AND(( domain = expression.AND(
domain, [('location_id', 'in', self.location_ids.ids)] (domain, [("location_id", "in", self.location_ids.ids)])
)) )
return domain return domain
def _start_inventory_values(self): def _start_inventory_values(self):
return { return {"state": "in_progress"}
'state': 'in_progress'
}
def _finish_inventory_values(self): def _finish_inventory_values(self):
return { return {"state": "finished"}
'state': 'finished'
}
def _close_inventory_values(self): def _close_inventory_values(self):
return { return {"state": "closed"}
'state': 'closed'
}
@api.model @api.model
def create(self, vals): def create(self, vals):
if vals.get('name', '/') == '/': if vals.get("name", "/") == "/":
vals['name'] = self.env['ir.sequence'].next_by_code( vals["name"] = self.env["ir.sequence"].next_by_code(
'stock.inventory.kanban') "stock.inventory.kanban"
)
return super().create(vals) return super().create(vals)
@api.multi
def calculate_kanbans(self): def calculate_kanbans(self):
for rec in self: for rec in self:
if rec.state == 'draft': if rec.state == "draft":
rec.kanban_ids = self.env['stock.request.kanban'].search( rec.kanban_ids = self.env["stock.request.kanban"].search(
rec._get_inventory_kanban_domain() rec._get_inventory_kanban_domain()
) )
@api.multi
def start_inventory(self): def start_inventory(self):
self.calculate_kanbans() self.calculate_kanbans()
self.write(self._start_inventory_values()) self.write(self._start_inventory_values())
@api.multi
def finish_inventory(self): def finish_inventory(self):
self.write(self._finish_inventory_values()) self.write(self._finish_inventory_values())
@api.multi
def close_inventory(self): def close_inventory(self):
self.write(self._close_inventory_values()) self.write(self._close_inventory_values())
@api.multi
def print_missing_kanbans(self): def print_missing_kanbans(self):
""" Print the missing kanban cards in order to restore them """ Print the missing kanban cards in order to restore them
""" """
self.ensure_one() self.ensure_one()
return self.env.ref( return self.env.ref("stock_request_kanban.action_report_kanban").report_action(
'stock_request_kanban.action_report_kanban').report_action(
self.missing_kanban_ids self.missing_kanban_ids
) )
def _cancel_inventory_values(self): def _cancel_inventory_values(self):
return { return {"state": "cancelled"}
'state': 'cancelled',
}
@api.multi
def cancel(self): def cancel(self):
self.write(self._cancel_inventory_values()) self.write(self._cancel_inventory_values())
def _to_draft_inventory_values(self): def _to_draft_inventory_values(self):
return { return {
'state': 'draft', "state": "draft",
'kanban_ids': [(5, 0)], "kanban_ids": [(5, 0)],
'scanned_kanban_ids': [(5, 0)], "scanned_kanban_ids": [(5, 0)],
} }
@api.multi
def to_draft(self): def to_draft(self):
self.write(self._to_draft_inventory_values()) self.write(self._to_draft_inventory_values())

View File

@@ -1,10 +1,10 @@
# Copyright 2018 Creu Blanca # Copyright 2018 Creu Blanca
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html). # License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
from odoo import models, fields from odoo import fields, models
class StockRequest(models.Model): class StockRequest(models.Model):
_inherit = 'stock.request' _inherit = "stock.request"
kanban_id = fields.Many2one('stock.request.kanban', readonly=True) kanban_id = fields.Many2one("stock.request.kanban", readonly=True)

View File

@@ -1,47 +1,52 @@
# Copyright 2018 Creu Blanca # Copyright 2018 Creu Blanca
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html). # License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
from odoo import api, models, fields, _
from odoo.exceptions import ValidationError
from reportlab.graphics.barcode import getCodes from reportlab.graphics.barcode import getCodes
from odoo import _, api, fields, models
from odoo.exceptions import ValidationError
class StockRequestKanban(models.Model): class StockRequestKanban(models.Model):
_name = 'stock.request.kanban' _name = "stock.request.kanban"
_description = 'Stock Request Kanban' _description = "Stock Request Kanban"
_inherit = 'stock.request.abstract' _inherit = "stock.request.abstract"
active = fields.Boolean(default=True) active = fields.Boolean(default=True)
@api.model @api.model
def create(self, vals): def create(self, vals):
if vals.get('name', '/') == '/': if vals.get("name", "/") == "/":
vals['name'] = self.env['ir.sequence'].next_by_code( vals["name"] = self.env["ir.sequence"].next_by_code("stock.request.kanban")
'stock.request.kanban')
return super().create(vals) return super().create(vals)
@api.model @api.model
def get_barcode_format(self): def get_barcode_format(self):
return self.env['ir.config_parameter'].sudo().get_param( return (
'stock_request_kanban.barcode_format', default='Standard39' self.env["ir.config_parameter"]
.sudo()
.get_param("stock_request_kanban.barcode_format", default="Standard39")
) )
@api.model @api.model
def _recompute_barcode(self, barcode): def _recompute_barcode(self, barcode):
if self.env['ir.config_parameter'].sudo().get_param( if (
'stock_request_kanban.crc', default='1' self.env["ir.config_parameter"]
) == '0': .sudo()
.get_param("stock_request_kanban.crc", default="1")
== "0"
):
return barcode return barcode
bcc = getCodes()[self.get_barcode_format()](value=barcode[:-1]) bcc = getCodes()[self.get_barcode_format()](value=barcode[:-1])
bcc.validate() bcc.validate()
bcc.encode() bcc.encode()
if bcc.encoded[1:-1] != barcode: if bcc.encoded[1:-1] != barcode:
raise ValidationError(_('CRC is not valid')) raise ValidationError(_("CRC is not valid"))
return barcode[:-1] return barcode[:-1]
@api.model @api.model
def search_barcode(self, barcode): def search_barcode(self, barcode):
recomputed_barcode = self._recompute_barcode(barcode) recomputed_barcode = self._recompute_barcode(barcode)
return self.env['stock.request.kanban'].search([ return self.env["stock.request.kanban"].search(
('name', '=', recomputed_barcode) [("name", "ilike", recomputed_barcode)]
]) )

View File

@@ -11,5 +11,4 @@
<field name="header_spacing">0</field> <field name="header_spacing">0</field>
<field name="dpi">72</field> <field name="dpi">72</field>
</record> </record>
</odoo>
</odoo>

View File

@@ -1,90 +1,103 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2017 Eficent <!-- Copyright 2017-2020 ForgeFlow, S.L.
License LGPL-3.0 or later (http://www.gnu.org/licenses/agpl). --> License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl). -->
<odoo> <odoo>
<template id="report_simple_label"> <template id="report_simple_label">
<div class="col-xs-3" style="padding:20;height:350px;"> <div class="col-3" style="padding:20;height:350px;">
<div class="container"> <div class="container">
<div class="row"> <div class="row">
<div class="col-xs-12" <div
style="height:15%;border:2px solid black;text-align:center;vertical-align:middle;display:table;"> class="col-12"
style="height:15%;border:2px solid black;text-align:center;vertical-align:middle;display:table;"
>
<div style="display: table-cell; vertical-align: middle;"> <div style="display: table-cell; vertical-align: middle;">
<strong t-field="o.location_id.name"/> <strong t-field="o.location_id.name" />
</div> </div>
</div> </div>
</div> </div>
<div class="row" <div class="row" t-if="o.product_id.default_code">
t-if="o.product_id.default_code"> <div
<div class="col-xs-12" class="col-12"
style="height:15%;border:2px solid black;text-align:center;vertical-align:middle;display:table;"> style="height:15%;border:2px solid black;text-align:center;vertical-align:middle;display:table;"
>
<div style="display: table-cell; vertical-align: middle;"> <div style="display: table-cell; vertical-align: middle;">
<strong t-field="o.product_id.default_code" <strong
t-if="o.product_id.default_code"/> t-field="o.product_id.default_code"
t-if="o.product_id.default_code"
/>
</div> </div>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-xs-12 align-middle" <div
style="height:40%;border:2px solid black;text-align:center;vertical-align:middle;display:table;"> class="col-12 align-middle"
style="height:40%;border:2px solid black;text-align:center;vertical-align:middle;display:table;"
>
<div style="display: table-cell; vertical-align: middle;"> <div style="display: table-cell; vertical-align: middle;">
<strong t-field="o.product_id.name"/> <strong t-field="o.product_id.name" />
</div> </div>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-xs-6" <div
style="height:10%;border:2px solid black;text-align:center;vertical-align:middle;display:table;"> class="col-6"
<div style="display: table-cell; vertical-align: middle;"> style="height:10%;border:2px solid black;text-align:center;vertical-align:middle;"
<span t-esc="float(o.product_qty)"/> >
<span t-field="o.product_id.uom_id.name"/> <div style="vertical-align: middle;">
<span t-esc="float(o.product_qty)" />
<span t-field="o.product_id.uom_id.name" />
</div> </div>
</div> </div>
<div class="col-xs-6" <div
style="height:10%;border:2px solid black;text-align:center;vertical-align:middle;display:table;"> class="col-6"
<div style="display: table-cell; vertical-align: middle;"> style="height:10%;border:2px solid black;text-align:center;vertical-align:middle;"
<span t-esc="float(o.product_uom_qty)"/> >
<span t-field="o.product_uom_id.name"/> <div style="vertical-align: middle;">
<span t-esc="float(o.product_uom_qty)" />
<span t-field="o.product_uom_id.name" />
</div> </div>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-xs-12" <div
style="height:10%;border:2px solid black;text-align:center;vertical-align:middle;display:table;"> class="col-12"
style="height:10%;border:2px solid black;text-align:center;vertical-align:middle;display:table;"
>
<div style="display: table-cell; vertical-align: middle;"> <div style="display: table-cell; vertical-align: middle;">
<span t-field="o.name"/> <span t-field="o.name" />
</div> </div>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-xs-12" style="height:10%;"> <div class="col-12" style="height:10%;">
<img t-att-src="'/report/barcode/%s/%s?width=%s&amp;height=%s' % (o.get_barcode_format(), o.name, 320, 20)" <img
style="height:80%; width: 100%;"/> t-att-src="'/report/barcode/%s/%s?width=%s&amp;height=%s' % (o.get_barcode_format(), o.name, 320, 20)"
style="height:80%; width: 100%;"
/>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</template> </template>
<template id="report_kanban_label"> <template id="report_kanban_label">
<t t-call="web.basic_layout"> <t t-call="web.basic_layout">
<t t-foreach="docs" t-as="o"> <t t-foreach="docs" t-as="o">
<div class="page"> <div class="page">
<t t-call="stock_request_kanban.report_simple_label"> <t t-call="stock_request_kanban.report_simple_label">
<t t-set="o" t-value="o"/> <t t-set="o" t-value="o" />
</t> </t>
</div> </div>
</t> </t>
</t> </t>
</template> </template>
<report <report
id="action_report_kanban" id="action_report_kanban"
string="Print kanban" string="Print kanban"
model="stock.request.kanban" model="stock.request.kanban"
report_type="qweb-pdf" report_type="qweb-pdf"
name="stock_request_kanban.report_kanban_label" name="stock_request_kanban.report_kanban_label"
file="stock_request_kanban.report_kanban_label" file="stock_request_kanban.report_kanban_label"
paperformat="stock_request_kanban.kanban_paper_format" paperformat="stock_request_kanban.kanban_paper_format"
menu="True"/> menu="True"
/>
</odoo> </odoo>

View File

@@ -0,0 +1,49 @@
odoo.define("stock_request_kanban.StockRequestKanbanController", function(require) {
"use strict";
var core = require("web.core");
var ListController = require("web.ListController");
var qweb = core.qweb;
var StockRequestKanbanController = ListController.extend({
// -------------------------------------------------------------------------
// Public
// -------------------------------------------------------------------------
init: function(parent, model, renderer) {
this.context = renderer.state.getContext();
return this._super.apply(this, arguments);
},
/**
* @override
*/
renderButtons: function($node) {
this._super.apply(this, arguments);
var $buttonScan = $(qweb.render("StockRequestKanban.Buttons"));
$buttonScan.on("click", this._onOpenWizard.bind(this));
$buttonScan.prependTo($node.find(".o_list_buttons"));
},
// -------------------------------------------------------------------------
// Handlers
// -------------------------------------------------------------------------
_onOpenWizard: function() {
var context = {
active_model: this.modelName,
};
this.do_action({
res_model: "wizard.stock.request.kanban",
views: [[false, "form"]],
target: "new",
type: "ir.actions.act_window",
context: context,
});
},
});
return StockRequestKanbanController;
});

View File

@@ -0,0 +1,17 @@
odoo.define("stock_request_kanban.StockRequestKanbanListView", function(require) {
"use strict";
var ListView = require("web.ListView");
var StockRequestKanbanController = require("stock_request_kanban.StockRequestKanbanController");
var viewRegistry = require("web.view_registry");
var StockRequestKanbanListView = ListView.extend({
config: _.extend({}, ListView.prototype.config, {
Controller: StockRequestKanbanController,
}),
});
viewRegistry.add("stock_request_kanban_list", StockRequestKanbanListView);
return StockRequestKanbanListView;
});

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<templates id="template" xml:space="preserve">
<button t-name="StockRequestKanban.Buttons" class="btn-primary btn" type="button">
Scan Kanban
</button>
</templates>

View File

@@ -1,16 +1,17 @@
# Copyright 2017 Creu Blanca # Copyright 2017 Creu Blanca
# Copyright 2017 Eficent Business and IT Consulting Services, S.L. # Copyright 2017-2020 ForgeFlow, S.L.
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html). # License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
from odoo.tests.common import TransactionCase
from reportlab.graphics.barcode import getCodes from reportlab.graphics.barcode import getCodes
from odoo.tests.common import TransactionCase
class TestBaseKanban(TransactionCase): class TestBaseKanban(TransactionCase):
def pass_code(self, wizard, code): def pass_code(self, wizard, code):
bcc = getCodes()[ bcc = getCodes()[self.env["stock.request.kanban"].get_barcode_format()](
self.env['stock.request.kanban'].get_barcode_format()](value=code) value=code
)
bcc.validate() bcc.validate()
bcc.encode() bcc.encode()
wizard.on_barcode_scanned(bcc.encoded[1:-1]) wizard.on_barcode_scanned(bcc.encoded[1:-1])

View File

@@ -1,5 +1,5 @@
# Copyright 2017 Creu Blanca # Copyright 2017 Creu Blanca
# Copyright 2017 Eficent Business and IT Consulting Services, S.L. # Copyright 2017-2020 ForgeFlow, S.L.
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html). # License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
from .base_test import TestBaseKanban from .base_test import TestBaseKanban
@@ -8,68 +8,78 @@ from .base_test import TestBaseKanban
class TestKanban(TestBaseKanban): class TestKanban(TestBaseKanban):
def setUp(self): def setUp(self):
super().setUp() super().setUp()
self.main_company = self.env.ref('base.main_company') self.main_company = self.env.ref("base.main_company")
self.route = self.env['stock.location.route'].create({ self.route = self.env["stock.location.route"].create(
'name': 'Transfer', {
'product_categ_selectable': False, "name": "Transfer",
'product_selectable': True, "product_categ_selectable": False,
'company_id': self.main_company.id, "product_selectable": True,
'sequence': 10, "company_id": self.main_company.id,
}) "sequence": 10,
self.product = self.env['product.product'].create({ }
'name': 'Product', )
'route_ids': [(4, self.route.id)], self.product = self.env["product.product"].create(
'company_id': False, {"name": "Product", "route_ids": [(4, self.route.id)], "company_id": False}
}) )
self.product_2 = self.env['product.product'].create({ self.product_2 = self.env["product.product"].create(
'name': 'Product 2', {
'route_ids': [(4, self.route.id)], "name": "Product 2",
'company_id': False, "route_ids": [(4, self.route.id)],
}) "company_id": False,
self.kanban_1 = self.env['stock.request.kanban'].create({ }
'product_id': self.product.id, )
'product_uom_id': self.product.uom_id.id, self.kanban_1 = self.env["stock.request.kanban"].create(
'product_uom_qty': 1, {
}) "product_id": self.product.id,
self.kanban_2 = self.env['stock.request.kanban'].create({ "product_uom_id": self.product.uom_id.id,
'product_id': self.product.id, "product_uom_qty": 1,
'product_uom_id': self.product.uom_id.id, }
'product_uom_qty': 1, )
}) self.kanban_2 = self.env["stock.request.kanban"].create(
self.kanban_3 = self.env['stock.request.kanban'].create({ {
'product_id': self.product_2.id, "product_id": self.product.id,
'product_uom_id': self.product.uom_id.id, "product_uom_id": self.product.uom_id.id,
'product_uom_qty': 1, "product_uom_qty": 1,
}) }
)
self.kanban_3 = self.env["stock.request.kanban"].create(
{
"product_id": self.product_2.id,
"product_uom_id": self.product.uom_id.id,
"product_uom_qty": 1,
}
)
def test_inventory_warehouse(self): def test_inventory_warehouse(self):
inventory = self.env['stock.inventory.kanban'].create({ inventory = self.env["stock.inventory.kanban"].create(
'warehouse_ids': [(4, self.kanban_1.warehouse_id.id)], {"warehouse_ids": [(4, self.kanban_1.warehouse_id.id)]}
}) )
inventory.start_inventory() inventory.start_inventory()
self.assertIn(self.kanban_1, inventory.kanban_ids) self.assertIn(self.kanban_1, inventory.kanban_ids)
self.assertIn(self.kanban_1, inventory.missing_kanban_ids) self.assertIn(self.kanban_1, inventory.missing_kanban_ids)
def test_inventory_location(self): def test_inventory_location(self):
inventory = self.env['stock.inventory.kanban'].create({ inventory = self.env["stock.inventory.kanban"].create(
'location_ids': [(4, self.kanban_1.location_id.id)], {"location_ids": [(4, self.kanban_1.location_id.id)]}
}) )
inventory.start_inventory() inventory.start_inventory()
self.assertIn(self.kanban_1, inventory.kanban_ids) self.assertIn(self.kanban_1, inventory.kanban_ids)
self.assertIn(self.kanban_1, inventory.missing_kanban_ids) self.assertIn(self.kanban_1, inventory.missing_kanban_ids)
def test_inventory_product(self): def test_inventory_product(self):
inventory = self.env['stock.inventory.kanban'].create({ inventory = self.env["stock.inventory.kanban"].create(
'product_ids': [(4, self.product.id)], {"product_ids": [(4, self.product.id)]}
}) )
inventory.start_inventory() inventory.start_inventory()
self.assertIn(self.kanban_1, inventory.kanban_ids) self.assertIn(self.kanban_1, inventory.kanban_ids)
self.assertNotIn(self.kanban_3, inventory.kanban_ids) self.assertNotIn(self.kanban_3, inventory.kanban_ids)
self.assertIn(self.kanban_1, inventory.missing_kanban_ids) self.assertIn(self.kanban_1, inventory.missing_kanban_ids)
self.assertEqual(inventory.state, 'in_progress') self.assertEqual(inventory.state, "in_progress")
wizard = self.env['wizard.stock.inventory.kanban'].with_context( wizard = (
default_inventory_kanban_id=inventory.id self.env["wizard.stock.inventory.kanban"]
).create({}) .with_context(default_inventory_kanban_id=inventory.id)
.create({})
)
self.pass_code(wizard, self.kanban_3.name) self.pass_code(wizard, self.kanban_3.name)
self.assertEqual(wizard.status_state, 1) self.assertEqual(wizard.status_state, 1)
self.pass_code(wizard, self.kanban_1.name) self.pass_code(wizard, self.kanban_1.name)
@@ -81,22 +91,22 @@ class TestKanban(TestBaseKanban):
self.assertNotIn(self.kanban_1, inventory.missing_kanban_ids) self.assertNotIn(self.kanban_1, inventory.missing_kanban_ids)
self.assertIn(self.kanban_1, inventory.scanned_kanban_ids) self.assertIn(self.kanban_1, inventory.scanned_kanban_ids)
inventory.finish_inventory() inventory.finish_inventory()
self.assertEqual(inventory.state, 'finished') self.assertEqual(inventory.state, "finished")
inventory.close_inventory() inventory.close_inventory()
self.assertEqual(inventory.state, 'closed') self.assertEqual(inventory.state, "closed")
def test_cancel_inventory(self): def test_cancel_inventory(self):
inventory = self.env['stock.inventory.kanban'].create({ inventory = self.env["stock.inventory.kanban"].create(
'product_ids': [(4, self.product.id)], {"product_ids": [(4, self.product.id)]}
}) )
inventory.start_inventory() inventory.start_inventory()
self.assertIn(self.kanban_1, inventory.kanban_ids) self.assertIn(self.kanban_1, inventory.kanban_ids)
self.assertNotIn(self.kanban_3, inventory.kanban_ids) self.assertNotIn(self.kanban_3, inventory.kanban_ids)
self.assertIn(self.kanban_1, inventory.missing_kanban_ids) self.assertIn(self.kanban_1, inventory.missing_kanban_ids)
self.assertEqual(inventory.state, 'in_progress') self.assertEqual(inventory.state, "in_progress")
inventory.cancel() inventory.cancel()
self.assertEqual(inventory.state, 'cancelled') self.assertEqual(inventory.state, "cancelled")
inventory.to_draft() inventory.to_draft()
self.assertEqual(inventory.state, 'draft') self.assertEqual(inventory.state, "draft")
self.assertFalse(inventory.kanban_ids) self.assertFalse(inventory.kanban_ids)
self.assertFalse(inventory.scanned_kanban_ids) self.assertFalse(inventory.scanned_kanban_ids)

View File

@@ -1,71 +1,74 @@
# Copyright 2017 Creu Blanca # Copyright 2017 Creu Blanca
# Copyright 2017 Eficent Business and IT Consulting Services, S.L. # Copyright 2017-2020 ForgeFlow, S.L.
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html). # License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
from odoo.exceptions import ValidationError from odoo.exceptions import ValidationError
from .base_test import TestBaseKanban from .base_test import TestBaseKanban
class TestKanban(TestBaseKanban): class TestKanban(TestBaseKanban):
def setUp(self): def setUp(self):
super().setUp() super().setUp()
self.main_company = self.env.ref('base.main_company') self.main_company = self.env.ref("base.main_company")
self.warehouse = self.env.ref('stock.warehouse0') self.warehouse = self.env.ref("stock.warehouse0")
self.categ_unit = self.env.ref('uom.product_uom_categ_unit') self.categ_unit = self.env.ref("uom.product_uom_categ_unit")
# common data # common data
self.company_2 = self.env['res.company'].create({ self.company_2 = self.env["res.company"].create({"name": "Comp2"})
'name': 'Comp2', self.wh2 = self.env["stock.warehouse"].search(
}) [("company_id", "=", self.company_2.id)], limit=1
self.wh2 = self.env['stock.warehouse'].search( )
[('company_id', '=', self.company_2.id)], limit=1) self.wh3 = self.env["stock.warehouse"].create(
self.wh3 = self.env['stock.warehouse'].create({ {
'name': 'Warehouse TEst', "name": "Warehouse TEst",
'code': 'WH-TEST', "code": "WH-TEST",
'company_id': self.main_company.id, "company_id": self.main_company.id,
}) }
)
self.ressuply_loc = self.env['stock.location'].create({ self.ressuply_loc = self.env["stock.location"].create(
'name': 'Ressuply', {"name": "Ressuply", "location_id": self.warehouse.view_location_id.id}
'location_id': self.warehouse.view_location_id.id, )
}) self.route = self.env["stock.location.route"].create(
self.route = self.env['stock.location.route'].create({ {
'name': 'Transfer', "name": "Transfer",
'product_categ_selectable': False, "product_categ_selectable": False,
'product_selectable': True, "product_selectable": True,
'company_id': self.main_company.id, "company_id": self.main_company.id,
'sequence': 10, "sequence": 10,
}) }
self.product = self.env['product.product'].create({ )
'name': 'Product', self.product = self.env["product.product"].create(
'route_ids': [(4, self.route.id)], {"name": "Product", "route_ids": [(4, self.route.id)], "company_id": False}
'company_id': False, )
}) self.uom_dozen = self.env["uom.uom"].create(
self.uom_dozen = self.env['uom.uom'].create({ {
'name': 'Test-DozenA', "name": "Test-DozenA",
'category_id': self.categ_unit.id, "category_id": self.categ_unit.id,
'factor_inv': 12, "factor_inv": 12,
'uom_type': 'bigger', "uom_type": "bigger",
'rounding': 0.001}) "rounding": 0.001,
}
)
self.env['stock.rule'].create({ self.env["stock.rule"].create(
'name': 'Transfer', {
'route_id': self.route.id, "name": "Transfer",
'location_src_id': self.ressuply_loc.id, "route_id": self.route.id,
'location_id': self.warehouse.lot_stock_id.id, "location_src_id": self.ressuply_loc.id,
'action': 'pull_push', "location_id": self.warehouse.lot_stock_id.id,
'picking_type_id': self.warehouse.int_type_id.id, "action": "pull_push",
'procure_method': 'make_to_stock', "picking_type_id": self.warehouse.int_type_id.id,
'warehouse_id': self.warehouse.id, "procure_method": "make_to_stock",
'company_id': self.main_company.id, "warehouse_id": self.warehouse.id,
'propagate': 'False', "company_id": self.main_company.id,
}) }
self.env['ir.config_parameter'].set_param( )
'stock_request_kanban.crc', '1') self.env["ir.config_parameter"].set_param("stock_request_kanban.crc", "1")
def test_onchanges(self): def test_onchanges(self):
kanban = self.env['stock.request.kanban'].new({}) kanban = self.env["stock.request.kanban"].new({})
kanban.product_id = self.product kanban.product_id = self.product
kanban.onchange_product_id() kanban.onchange_product_id()
kanban.company_id = self.main_company kanban.company_id = self.main_company
@@ -80,7 +83,7 @@ class TestKanban(TestBaseKanban):
self.assertEqual(kanban.warehouse_id, self.warehouse) self.assertEqual(kanban.warehouse_id, self.warehouse)
def test_create(self): def test_create(self):
kanban = self.env['stock.request.kanban'].new({}) kanban = self.env["stock.request.kanban"].new({})
kanban.product_id = self.product kanban.product_id = self.product
kanban.onchange_product_id() kanban.onchange_product_id()
kanban.product_uom_qty = 1 kanban.product_uom_qty = 1
@@ -89,95 +92,109 @@ class TestKanban(TestBaseKanban):
self.assertEqual(kanban.route_ids, self.route) self.assertEqual(kanban.route_ids, self.route)
def test_order_barcodes(self): def test_order_barcodes(self):
kanban_1 = self.env['stock.request.kanban'].create({ kanban_1 = self.env["stock.request.kanban"].create(
'product_id': self.product.id, {
'product_uom_id': self.product.uom_id.id, "product_id": self.product.id,
'product_uom_qty': 1, "product_uom_id": self.product.uom_id.id,
}) "product_uom_qty": 1,
kanban_2 = self.env['stock.request.kanban'].create({ }
'product_id': self.product.id, )
'product_uom_id': self.product.uom_id.id, kanban_2 = self.env["stock.request.kanban"].create(
'product_uom_qty': 1, {
}) "product_id": self.product.id,
kanban_3 = self.env['stock.request.kanban'].create({ "product_uom_id": self.product.uom_id.id,
'product_id': self.product.id, "product_uom_qty": 1,
'product_uom_id': self.product.uom_id.id, }
'product_uom_qty': 1, )
'company_id': self.main_company.id, kanban_3 = self.env["stock.request.kanban"].create(
'warehouse_id': self.wh3.id, {
'location_id': self.wh3.lot_stock_id.id, "product_id": self.product.id,
}) "product_uom_id": self.product.uom_id.id,
order = self.env['stock.request.order'].create({ "product_uom_qty": 1,
'company_id': self.main_company.id, "company_id": self.main_company.id,
'warehouse_id': self.warehouse.id, "warehouse_id": self.wh3.id,
'location_id': self.warehouse.lot_stock_id.id, "location_id": self.wh3.lot_stock_id.id,
}) }
wizard = self.env['wizard.stock.request.order.kanban'].with_context( )
default_order_id=order.id order = self.env["stock.request.order"].create(
).create({}) {
"company_id": self.main_company.id,
"warehouse_id": self.warehouse.id,
"location_id": self.warehouse.lot_stock_id.id,
}
)
wizard = (
self.env["wizard.stock.request.order.kanban"]
.with_context(default_order_id=order.id)
.create({})
)
with self.assertRaises(ValidationError): with self.assertRaises(ValidationError):
wizard.on_barcode_scanned(kanban_1.name) wizard.on_barcode_scanned(kanban_1.name)
self.pass_code(wizard, kanban_1.name) self.pass_code(wizard, kanban_1.name)
self.assertEqual(wizard.status_state, 0) self.assertEqual(wizard.status_state, 0)
self.assertTrue(order.stock_request_ids.filtered( self.assertTrue(
lambda r: r.kanban_id == kanban_1 order.stock_request_ids.filtered(lambda r: r.kanban_id == kanban_1)
)) )
self.pass_code(wizard, kanban_2.name) self.pass_code(wizard, kanban_2.name)
self.assertTrue(order.stock_request_ids.filtered( self.assertTrue(
lambda r: r.kanban_id == kanban_2 order.stock_request_ids.filtered(lambda r: r.kanban_id == kanban_2)
)) )
self.assertEqual(wizard.status_state, 0) self.assertEqual(wizard.status_state, 0)
self.pass_code(wizard, kanban_1.name) self.pass_code(wizard, kanban_1.name)
self.assertEqual(wizard.status_state, 1) self.assertEqual(wizard.status_state, 1)
self.pass_code(wizard, kanban_2.name+kanban_1.name) self.pass_code(wizard, kanban_2.name + kanban_1.name)
self.assertEqual(wizard.status_state, 1) self.assertEqual(wizard.status_state, 1)
with self.assertRaises(ValidationError): with self.assertRaises(ValidationError):
self.pass_code(wizard, kanban_3.name) self.pass_code(wizard, kanban_3.name)
def test_barcodes(self): def test_barcodes(self):
kanban_1 = self.env['stock.request.kanban'].create({ kanban_1 = self.env["stock.request.kanban"].create(
'product_id': self.product.id, {
'product_uom_id': self.product.uom_id.id, "product_id": self.product.id,
'product_uom_qty': 1, "product_uom_id": self.product.uom_id.id,
}) "product_uom_qty": 1,
kanban_2 = self.env['stock.request.kanban'].create({ }
'product_id': self.product.id, )
'product_uom_id': self.product.uom_id.id, kanban_2 = self.env["stock.request.kanban"].create(
'product_uom_qty': 1, {
}) "product_id": self.product.id,
kanban_3 = self.env['stock.request.kanban'].create({ "product_uom_id": self.product.uom_id.id,
'product_id': self.product.id, "product_uom_qty": 1,
'product_uom_id': self.product.uom_id.id, }
'product_uom_qty': 1, )
}) kanban_3 = self.env["stock.request.kanban"].create(
wizard = self.env['wizard.stock.request.kanban'].with_context( {
).create({}) "product_id": self.product.id,
"product_uom_id": self.product.uom_id.id,
"product_uom_qty": 1,
}
)
wizard = self.env["wizard.stock.request.kanban"].with_context().create({})
with self.assertRaises(ValidationError): with self.assertRaises(ValidationError):
wizard.on_barcode_scanned(kanban_1.name) wizard.on_barcode_scanned(kanban_1.name)
self.assertFalse(self.env['stock.request'].search( self.assertFalse(
[('kanban_id', '=', kanban_1.id)]) self.env["stock.request"].search([("kanban_id", "=", kanban_1.id)])
) )
self.pass_code(wizard, kanban_1.name) self.pass_code(wizard, kanban_1.name)
self.assertEqual(wizard.status_state, 0) self.assertEqual(wizard.status_state, 0)
self.assertTrue(self.env['stock.request'].search( self.assertTrue(
[('kanban_id', '=', kanban_1.id)]) self.env["stock.request"].search([("kanban_id", "=", kanban_1.id)])
) )
self.assertFalse(self.env['stock.request'].search( self.assertFalse(
[('kanban_id', '=', kanban_2.id)]) self.env["stock.request"].search([("kanban_id", "=", kanban_2.id)])
) )
self.pass_code(wizard, kanban_2.name) self.pass_code(wizard, kanban_2.name)
self.assertTrue(self.env['stock.request'].search( self.assertTrue(
[('kanban_id', '=', kanban_2.id)]) self.env["stock.request"].search([("kanban_id", "=", kanban_2.id)])
) )
with self.assertRaises(ValidationError): with self.assertRaises(ValidationError):
wizard.on_barcode_scanned(kanban_3.name) wizard.on_barcode_scanned(kanban_3.name)
self.assertFalse(self.env['stock.request'].search( self.assertFalse(
[('kanban_id', '=', kanban_3.id)]) self.env["stock.request"].search([("kanban_id", "=", kanban_3.id)])
) )
self.env['ir.config_parameter'].set_param( self.env["ir.config_parameter"].set_param("stock_request_kanban.crc", "0")
'stock_request_kanban.crc', '0')
wizard.on_barcode_scanned(kanban_3.name) wizard.on_barcode_scanned(kanban_3.name)
self.assertEqual(wizard.status_state, 0) self.assertEqual(wizard.status_state, 0)
self.assertTrue(self.env['stock.request'].search( self.assertTrue(
[('kanban_id', '=', kanban_3.id)]) self.env["stock.request"].search([("kanban_id", "=", kanban_3.id)])
) )

View File

@@ -0,0 +1,57 @@
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2020 ForgeFlow, S.L.
License LGPL-3.0 or later (https://www.gnu.org/licenses/gpl). -->
<odoo>
<record id="product_product_form_view_kanban_card_button" model="ir.ui.view">
<field name="name">product.product.stock.request.kanban</field>
<field name="model">product.product</field>
<field name="inherit_id" ref="product.product_normal_form_view" />
<field
name="groups_id"
eval="[(4, ref('stock_request.group_stock_request_user'))]"
/>
<field name="arch" type="xml">
<div name="button_box" position="inside">
<button
class="oe_stat_button"
name="action_view_kanban_cards"
type="object"
attrs="{'invisible':[('type', 'not in', ['product', 'consu'])]}"
icon="fa-barcode"
>
<field
string="Kanban Cards"
name="kanban_card_count"
widget="statinfo"
/>
</button>
</div>
</field>
</record>
<record id="product_template_form_view_bom_button" model="ir.ui.view">
<field name="name">product.template.stock.request.kanban</field>
<field name="model">product.template</field>
<field name="inherit_id" ref="product.product_template_only_form_view" />
<field
name="groups_id"
eval="[(4, ref('stock_request.group_stock_request_user'))]"
/>
<field name="arch" type="xml">
<div name="button_box" position="inside">
<button
class="oe_stat_button"
name="action_view_kanban_cards"
type="object"
attrs="{'invisible':[('type', 'not in', ['product', 'consu'])]}"
icon="fa-barcode"
>
<field
string="Kanban Cards"
name="kanban_card_count"
widget="statinfo"
/>
</button>
</div>
</field>
</record>
</odoo>

View File

@@ -1,97 +1,122 @@
<?xml version="1.0" encoding="UTF-8" ?> <?xml version="1.0" encoding="UTF-8" ?>
<odoo> <odoo>
<record id="view_stock_inventory_kanban_tree" model="ir.ui.view"> <record id="view_stock_inventory_kanban_tree" model="ir.ui.view">
<field name="name">stock.inventory.kanban.tree</field> <field name="name">stock.inventory.kanban.tree</field>
<field name="model">stock.inventory.kanban</field> <field name="model">stock.inventory.kanban</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<tree string="Stock Inventories"> <tree string="Stock Inventories">
<field name="name"/> <field name="name" />
<field name="count_missing_kanbans"/> <field name="count_missing_kanbans" />
<field name="state"/> <field name="state" />
</tree> </tree>
</field> </field>
</record> </record>
<record id="view_stock_inventory_kanban_form" model="ir.ui.view"> <record id="view_stock_inventory_kanban_form" model="ir.ui.view">
<field name="name">stock.inventory.kanban.form</field> <field name="name">stock.inventory.kanban.form</field>
<field name="model">stock.inventory.kanban</field> <field name="model">stock.inventory.kanban</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<form string="Stock Inventories"> <form string="Stock Inventories">
<header> <header>
<button name="start_inventory" type="object" string="Start" <button
states="draft" class="btn-primary" name="start_inventory"
/> type="object"
<button name="finish_inventory" type="object" string="Finish" string="Start"
states="in_progress" class="btn-primary" states="draft"
/> class="btn-primary"
<button name="print_missing_kanbans" type="object" string="Print missing" />
states="finished" <button
/> name="finish_inventory"
<button name="close_inventory" type="object" string="Close" type="object"
states="finished" class="btn-primary" string="Finish"
/> states="in_progress"
<button name="cancel" type="object" string="Cancel" class="btn-primary"
states="draft,in_progress,finished" />
/> <button
<button name="to_draft" type="object" string="Set to draft" name="print_missing_kanbans"
states="cancelled" type="object"
/> string="Print missing"
<field name="state" widget="statusbar"/> states="finished"
/>
<button
name="close_inventory"
type="object"
string="Close"
states="finished"
class="btn-primary"
/>
<button
name="cancel"
type="object"
string="Cancel"
states="draft,in_progress,finished"
/>
<button
name="to_draft"
type="object"
string="Set to draft"
states="cancelled"
/>
<field name="state" widget="statusbar" />
</header> </header>
<sheet> <sheet>
<div class="oe_button_box" name="button_box"> <div class="oe_button_box" name="button_box">
<button name="%(stock_request_kanban.wizard_stock_inventory_kanban_action)d" <button
type="action" name="%(stock_request_kanban.wizard_stock_inventory_kanban_action)d"
icon="fa-barcode" type="action"
string="Scan" icon="fa-barcode"
states="in_progress" string="Scan"
class="oe_read_only" states="in_progress"
class="oe_read_only"
/> />
</div> </div>
<div class="oe_title"> <div class="oe_title">
<label for="name" string="Inventory Kanban" /> <label for="name" string="Inventory Kanban" />
<h1> <h1>
<field name="name" readonly="1"/> <field name="name" readonly="1" />
</h1> </h1>
</div> </div>
<group> <group>
<field name="warehouse_ids" widget="many2many_tags" groups="stock.group_stock_multi_locations"/> <field
<field name="location_ids" widget="many2many_tags" groups="stock.group_stock_multi_locations"/> name="warehouse_ids"
<field name="product_ids" widget="many2many_tags"/> widget="many2many_tags"
groups="stock.group_stock_multi_locations"
/>
<field
name="location_ids"
widget="many2many_tags"
groups="stock.group_stock_multi_locations"
/>
<field name="product_ids" widget="many2many_tags" />
</group> </group>
<notebook> <notebook>
<page string="Kanban" id="kanban"> <page string="Kanban" id="kanban">
<field name="kanban_ids"/> <field name="kanban_ids" />
</page> </page>
<page string="Missing" id="missing"> <page string="Missing" id="missing">
<field name="missing_kanban_ids"/> <field name="missing_kanban_ids" />
</page> </page>
<page string="Scanned" id="scanned"> <page string="Scanned" id="scanned">
<field name="scanned_kanban_ids"/> <field name="scanned_kanban_ids" />
</page> </page>
</notebook> </notebook>
</sheet> </sheet>
<div class="oe_chatter"> <div class="oe_chatter">
<field name="message_follower_ids" widget="mail_followers"/> <field name="message_follower_ids" widget="mail_followers" />
<field name="activity_ids" widget="mail_activity"/> <field name="activity_ids" widget="mail_activity" />
<field name="message_ids" widget="mail_thread"/> <field name="message_ids" widget="mail_thread" />
</div> </div>
</form> </form>
</field> </field>
</record> </record>
<record id="stock_inventory_kanban_action" model="ir.actions.act_window"> <record id="stock_inventory_kanban_action" model="ir.actions.act_window">
<field name="name">Stock Inventory Kanbans</field> <field name="name">Stock Inventory Kanbans</field>
<field name="res_model">stock.inventory.kanban</field> <field name="res_model">stock.inventory.kanban</field>
<field name="type">ir.actions.act_window</field> <field name="type">ir.actions.act_window</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field> <field name="view_mode">tree,form</field>
<field name="help" type="html"> <field name="help" type="html">
<p class="oe_view_nocontent_create"> <p class="oe_view_nocontent_create">
Click to add a Stock Inventory Kanban. Click to add a Stock Inventory Kanban.
</p> </p>
</field> </field>
</record> </record>
</odoo> </odoo>

View File

@@ -1,145 +1,198 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2017 Eficent <!-- Copyright 2017-2020 ForgeFlow, S.L.
License LGPL-3.0 or later (http://www.gnu.org/licenses/agpl). --> License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl). -->
<odoo> <odoo>
<record id="view_stock_request_kanban_tree" model="ir.ui.view"> <record id="view_stock_request_kanban_tree" model="ir.ui.view">
<field name="name">stock.request.kanban.tree</field> <field name="name">stock.request.kanban.tree</field>
<field name="model">stock.request.kanban</field> <field name="model">stock.request.kanban</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<tree string="Stock Requests" decoration-muted="active == 'false'" decoration-bf="message_needaction==True"> <tree
<field name="message_needaction" invisible="1"/> string="Stock Requests"
<field name="name"/> decoration-muted="active == 'false'"
<field name="warehouse_id" groups="stock.group_stock_multi_locations"/> decoration-bf="message_needaction==True"
<field name="location_id" groups="stock.group_stock_multi_locations"/> >
<field name="route_id" options="{'no_create': True}" groups="stock.group_stock_multi_locations"/> <field name="message_needaction" invisible="1" />
<field name="product_id"/> <field name="name" />
<field name="product_uom_id" <field name="warehouse_id" groups="stock.group_stock_multi_locations" />
options="{'no_open': True, 'no_create': True}" groups="uom.group_uom"/> <field name="location_id" groups="stock.group_stock_multi_locations" />
<field name="product_uom_qty"/> <field
<field name="active" invisible="1"/> name="route_id"
options="{'no_create': True}"
groups="stock.group_stock_multi_locations"
/>
<field name="product_id" />
<field
name="product_uom_id"
options="{'no_open': True, 'no_create': True}"
groups="uom.group_uom"
/>
<field name="product_uom_qty" />
<field name="active" invisible="1" />
</tree> </tree>
</field> </field>
</record> </record>
<record model="ir.ui.view" id="stock_request_kanban_search"> <record model="ir.ui.view" id="stock_request_kanban_search">
<field name="name">stock.request.kanban.search</field> <field name="name">stock.request.kanban.search</field>
<field name="model">stock.request.kanban</field> <field name="model">stock.request.kanban</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<search string="Stock Requests Kanban"> <search string="Stock Requests Kanban">
<field name="name" string="Stock Requests Kanban"/> <field name="name" string="Stock Requests Kanban" />
<field name="warehouse_id" groups="stock.group_stock_multi_locations"/> <field name="warehouse_id" groups="stock.group_stock_multi_locations" />
<field name="location_id" groups="stock.group_stock_multi_locations"/> <field name="location_id" groups="stock.group_stock_multi_locations" />
<field name="company_id" groups="base.group_multi_company"/> <field name="company_id" groups="base.group_multi_company" />
<field name="product_id"/> <field name="product_id" />
<filter string="Archived" name="inactive" domain="[('active','=',False)]"/> <filter
string="Archived"
name="inactive"
domain="[('active','=',False)]"
/>
<group expand="0" string="Group By"> <group expand="0" string="Group By">
<filter name="warehouse" string="Warehouse" domain="[]" context="{'group_by':'warehouse_id'}"/> <filter
<filter name="location" string="Location" domain="[]" context="{'group_by':'location_id'}"/> name="warehouse"
string="Warehouse"
domain="[]"
context="{'group_by':'warehouse_id'}"
/>
<filter
name="location"
string="Location"
domain="[]"
context="{'group_by':'location_id'}"
/>
</group> </group>
</search> </search>
</field> </field>
</record> </record>
<record id="view_stock_request_kanban_form" model="ir.ui.view"> <record id="view_stock_request_kanban_form" model="ir.ui.view">
<field name="name">stock.request.kanban.form</field> <field name="name">stock.request.kanban.form</field>
<field name="model">stock.request.kanban</field> <field name="model">stock.request.kanban</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<form string="Stock Requests"> <form string="Stock Requests">
<header/> <header />
<sheet> <sheet>
<div class="oe_button_box" name="button_box"/> <div class="oe_button_box" name="button_box" />
<div class="oe_title"> <div class="oe_title">
<label for="name" string="Stock Request Kanban" /> <label for="name" string="Stock Request Kanban" />
<h1> <h1>
<field name="name" readonly="1"/> <field name="name" readonly="1" />
</h1> </h1>
</div> </div>
<group> <group>
<group> <group>
<field name="product_id"/> <field name="product_id" />
</group> </group>
<group> <group>
<field name="warehouse_id" widget="selection" groups="stock.group_stock_multi_locations"/> <field
<field name="location_id" groups="stock.group_stock_multi_locations"/> name="warehouse_id"
<field name="route_id" widget="selection"
options="{'no_create': True}" groups="stock.group_stock_multi_locations"/> groups="stock.group_stock_multi_locations"
<field name="route_ids" invisible="1"/> />
<field name="procurement_group_id" <field
groups="stock.group_adv_location"/> name="location_id"
<field name="company_id" groups="base.group_multi_company" options="{'no_create': True}"/> groups="stock.group_stock_multi_locations"
/>
<field
name="route_id"
options="{'no_create': True}"
groups="stock.group_stock_multi_locations"
/>
<field name="route_ids" invisible="1" />
<field
name="procurement_group_id"
groups="stock.group_adv_location"
/>
<field
name="company_id"
groups="base.group_multi_company"
options="{'no_create': True}"
/>
</group> </group>
<group name="quantities"> <group name="quantities">
<label for="product_uom_qty"/> <label for="product_uom_qty" />
<div> <div>
<field name="product_uom_qty" <field name="product_uom_qty" class="oe_inline" />
class="oe_inline"/> <field
<field name="product_uom_id" name="product_uom_id"
class="oe_inline" class="oe_inline"
options="{'no_open': True, 'no_create': True}" options="{'no_open': True, 'no_create': True}"
groups="uom.group_uom"/> groups="uom.group_uom"
/>
</div> </div>
</group> </group>
</group> </group>
</sheet> </sheet>
<div class="oe_chatter"> <div class="oe_chatter">
<field name="message_follower_ids" widget="mail_followers"/> <field name="message_follower_ids" widget="mail_followers" />
<field name="activity_ids" widget="mail_activity"/> <field name="activity_ids" widget="mail_activity" />
<field name="message_ids" widget="mail_thread"/> <field name="message_ids" widget="mail_thread" />
</div> </div>
</form> </form>
</field> </field>
</record> </record>
<!-- Partner Kanban View --> <!-- Partner Kanban View -->
<record model="ir.ui.view" id="stock_request_kanban_kanban_view"> <record model="ir.ui.view" id="stock_request_kanban_kanban_view">
<field name="name">stock.request.kanban.kanban</field> <field name="name">stock.request.kanban.kanban</field>
<field name="model">stock.request.kanban</field> <field name="model">stock.request.kanban</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<kanban class="o_res_partner_kanban"> <kanban class="o_res_partner_kanban">
<field name="id"/> <field name="id" />
<field name="name"/> <field name="name" />
<field name="product_id"/> <field name="product_id" />
<field name="display_name"/> <field name="display_name" />
<field name="warehouse_id"/> <field name="warehouse_id" />
<field name="location_id"/> <field name="location_id" />
<field name="product_qty"/> <field name="product_qty" />
<templates> <templates>
<t t-name="kanban-box"> <t t-name="kanban-box">
<div class="oe_kanban_global_click o_res_partner_kanban"> <div class="oe_kanban_global_click o_res_partner_kanban">
<div class="o_kanban_image">
<div class="o_kanban_image"> <img
<img t-att-src="kanban_image('product.product', 'image_small', record.product_id.raw_value)" alt="Kanban Image"/> t-att-src="kanban_image('product.product', 'image_small', record.product_id.raw_value)"
</div> alt="Kanban Image"
<div class="oe_kanban_details"> />
<strong class="o_kanban_record_title"><field name="display_name"/></strong>
<div class="o_kanban_tags_section"/>
<ul>
<li t-if="record.product_id.raw_value"><field name="product_id"/></li>
<li t-if="record.warehouse_id.raw_value" groups="stock.group_stock_multi_locations"><field name="warehouse_id"/></li>
<li t-if="record.location_id.raw_value" groups="stock.group_stock_multi_locations"><field name="location_id"/></li>
<li t-if="record.product_qty"><field name="product_qty"/> <field name="product_uom_id"/></li>
</ul>
</div>
</div> </div>
<div class="oe_kanban_details">
</t> <strong class="o_kanban_record_title">
</templates> <field name="display_name" />
</kanban> </strong>
</field> <div class="o_kanban_tags_section" />
</record> <ul>
<li t-if="record.product_id.raw_value">
<field name="product_id" />
</li>
<li
t-if="record.warehouse_id.raw_value"
groups="stock.group_stock_multi_locations"
>
<field name="warehouse_id" />
</li>
<li
t-if="record.location_id.raw_value"
groups="stock.group_stock_multi_locations"
>
<field name="location_id" />
</li>
<li t-if="record.product_qty">
<field name="product_qty" />
<field name="product_uom_id" />
</li>
</ul>
</div>
</div>
</t>
</templates>
</kanban>
</field>
</record>
<record id="stock_request_kanban_action" model="ir.actions.act_window"> <record id="stock_request_kanban_action" model="ir.actions.act_window">
<field name="name">Stock Request Kanbans</field> <field name="name">Stock Request Kanbans</field>
<field name="res_model">stock.request.kanban</field> <field name="res_model">stock.request.kanban</field>
<field name="type">ir.actions.act_window</field> <field name="type">ir.actions.act_window</field>
<field name="view_type">form</field>
<field name="view_mode">kanban,tree,form</field> <field name="view_mode">kanban,tree,form</field>
<field name="help" type="html"> <field name="help" type="html">
<p class="oe_view_nocontent_create"> <p class="oe_view_nocontent_create">
Click to add a Stock Request Kanban. Click to add a Stock Request Kanban.
</p> </p>
</field> </field>
</record> </record>
</odoo> </odoo>

View File

@@ -1,22 +1,25 @@
<?xml version="1.0" encoding="UTF-8" ?> <?xml version="1.0" encoding="UTF-8" ?>
<odoo> <odoo>
<menuitem
<menuitem id="stock_request_kanban_menu" id="stock_request_kanban_menu"
name="Kanban cards" name="Kanban cards"
parent="stock_request.menu_stock_request_master_data" parent="stock_request.menu_stock_request_master_data"
action="stock_request_kanban_action" action="stock_request_kanban_action"
groups="stock_request.group_stock_request_manager" groups="stock_request.group_stock_request_manager"
sequence="110"/> sequence="110"
/>
<menuitem id="menu_wizard_stock_request_kanban" <menuitem
name="Request from Kanban cards" id="menu_wizard_stock_request_kanban"
parent="stock_request.menu_stock_request_operations" name="Request from Kanban cards"
action="wizard_stock_request_kanban_action" parent="stock_request.menu_stock_request_operations"
sequence="40"/> action="wizard_stock_request_kanban_action"
sequence="40"
<menuitem id="menu_wizard_stock_inventory_kanban" />
name="Inventory" <menuitem
parent="stock_request.menu_stock_request_operations" id="menu_wizard_stock_inventory_kanban"
action="stock_inventory_kanban_action" name="Inventory"
sequence="60"/> parent="stock_request.menu_stock_request_operations"
action="stock_inventory_kanban_action"
sequence="60"
/>
</odoo> </odoo>

View File

@@ -1,21 +1,20 @@
<?xml version="1.0" encoding="UTF-8" ?> <?xml version="1.0" encoding="UTF-8" ?>
<odoo> <odoo>
<record model="ir.ui.view" id="stock_request_order_form"> <record model="ir.ui.view" id="stock_request_order_form">
<field name="name">stock.request.order.form</field> <field name="name">stock.request.order.form</field>
<field name="model">stock.request.order</field> <field name="model">stock.request.order</field>
<field name="inherit_id" ref="stock_request.stock_request_order_form"/> <field name="inherit_id" ref="stock_request.stock_request_order_form" />
<field name="arch" type="xml"> <field name="arch" type="xml">
<xpath expr="//div[@name='button_box']" position="inside"> <xpath expr="//div[@name='button_box']" position="inside">
<button name="%(stock_request_kanban.wizard_stock_request_order_kanban_action)d" <button
type="action" name="%(stock_request_kanban.wizard_stock_request_order_kanban_action)d"
icon="fa-barcode" type="action"
string="Scan" icon="fa-barcode"
states="draft" string="Scan"
class="oe_read_only" states="draft"
class="oe_read_only"
/> />
</xpath> </xpath>
</field> </field>
</record> </record>
</odoo> </odoo>

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2017 Eficent
License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl). -->
<odoo>
<template
id="stock_request_kanban_assets_backend"
name="stock_request_kanban assets"
inherit_id="web.assets_backend"
>
<xpath expr="." position="inside">
<script
type="text/javascript"
src="/stock_request_kanban/static/src/js/stock_request_kanban_scan_controller.js"
/>
<script
type="text/javascript"
src="/stock_request_kanban/static/src/js/stock_request_kanban_scan_view.js"
/>
</xpath>
</template>
<record id="view_stock_request_tree" model="ir.ui.view">
<field name="name">stock.request.tree</field>
<field name="model">stock.request</field>
<field name="inherit_id" ref="stock_request.view_stock_request_tree" />
<field name="arch" type="xml">
<tree position="attributes">
<attribute name="js_class">stock_request_kanban_list</attribute>
</tree>
</field>
</record>
</odoo>

View File

@@ -1,24 +1,20 @@
# Copyright 2017 Creu Blanca # Copyright 2017 Creu Blanca
# Copyright 2017 Eficent Business and IT Consulting Services, S.L. # Copyright 2017-2020 ForgeFlow, S.L.
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html). # License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
from odoo import fields, models, _ from odoo import _, fields, models
class WizardStockRequestOrderKanbanAbstract(models.TransientModel): class WizardStockInventoryKanban(models.TransientModel):
_name = "wizard.stock.inventory.kanban" _name = "wizard.stock.inventory.kanban"
_description = "Stock Inventory Kanban Wizard"
_inherit = "wizard.stock.request.kanban.abstract" _inherit = "wizard.stock.request.kanban.abstract"
inventory_kanban_id = fields.Many2one( inventory_kanban_id = fields.Many2one("stock.inventory.kanban", readonly=True)
'stock.inventory.kanban',
readonly=True,
)
def barcode_ending(self): def barcode_ending(self):
super().barcode_ending() super().barcode_ending()
self.inventory_kanban_id.write({ self.inventory_kanban_id.write({"scanned_kanban_ids": [(4, self.kanban_id.id)]})
'scanned_kanban_ids': [(4, self.kanban_id.id)]
})
def validate_kanban(self, barcode): def validate_kanban(self, barcode):
res = super().validate_kanban(barcode) res = super().validate_kanban(barcode)

View File

@@ -1,51 +1,56 @@
<?xml version="1.0"?> <?xml version="1.0" ?>
<!-- <!--
Copyright 2017 Eficent Business and IT Consulting Services, S.L. Copyright 2017-2020 ForgeFlow, S.L.
Copyright 2017 Creu Blanca Copyright 2017 Creu Blanca
License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html). License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
--> -->
<odoo> <odoo>
<record id="wizard_stock_inventory_kanban_form" model="ir.ui.view"> <record id="wizard_stock_inventory_kanban_form" model="ir.ui.view">
<field name="name">wizard.stock.inventory.kanban.form</field> <field name="name">wizard.stock.inventory.kanban.form</field>
<field name="model">wizard.stock.inventory.kanban</field> <field name="model">wizard.stock.inventory.kanban</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<form string="Add Kanban"> <form string="Add Kanban">
<div class="alert alert-success text-center o_form_header" <div
role="alert" style="margin-bottom:0px;" class="alert alert-success text-center o_form_header"
attrs="{'invisible':[('status_state', '!=', 0)]}"> role="alert"
<bold><field name="status"/></bold> style="margin-bottom:0px;"
attrs="{'invisible':[('status_state', '!=', 0)]}"
>
<bold>
<field name="status" />
</bold>
</div> </div>
<div class="alert alert-danger alert-dismissable text-center" <div
role="alert" style="margin-bottom:0px;" class="alert alert-danger alert-dismissable text-center"
attrs="{'invisible':[('status_state', '=', 0)]}"> role="alert"
<bold><field name="status"/></bold> style="margin-bottom:0px;"
attrs="{'invisible':[('status_state', '=', 0)]}"
>
<bold>
<field name="status" />
</bold>
</div> </div>
<field name="status_state" invisible="1"/> <field name="status_state" invisible="1" />
<field name="kanban_id" invisible="1"/> <field name="kanban_id" invisible="1" />
<field name="inventory_kanban_id" invisible="1"/> <field name="inventory_kanban_id" invisible="1" />
<field name="_barcode_scanned" widget="barcode_handler" <field name="_barcode_scanned" widget="barcode_handler" />
invisible="1"/>
<footer> <footer>
<button <button
name="action_cancel" name="action_cancel"
string="Close" string="Close"
class="oe_link" class="oe_link"
special="cancel" special="cancel"
/> />
</footer> </footer>
</form> </form>
</field> </field>
</record> </record>
<act_window
<act_window id="wizard_stock_inventory_kanban_action" id="wizard_stock_inventory_kanban_action"
name="Add Kanban" name="Add Kanban"
res_model="wizard.stock.inventory.kanban" res_model="wizard.stock.inventory.kanban"
view_mode="form" view_mode="form"
view_type="form" context="{'default_inventory_kanban_id': active_id}"
context="{'default_inventory_kanban_id': active_id}" target="new"
target="new"/> />
</odoo> </odoo>

View File

@@ -1,44 +1,51 @@
# Copyright 2017 Creu Blanca # Copyright 2017 Creu Blanca
# Copyright 2017 Eficent Business and IT Consulting Services, S.L. # Copyright 2017-2020 ForgeFlow, S.L.
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html). # License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
from odoo import fields, models, _ from odoo import _, fields, models
class WizardStockRequestOrderKanbanAbstract(models.TransientModel): class WizardStockRequestKanban(models.TransientModel):
_name = "wizard.stock.request.kanban" _name = "wizard.stock.request.kanban"
_description = "Stock Request Kanban Wizard"
_inherit = "wizard.stock.request.kanban.abstract" _inherit = "wizard.stock.request.kanban.abstract"
stock_request_id = fields.Many2one( stock_request_id = fields.Many2one("stock.request", readonly=True)
'stock.request',
readonly=True,
)
def barcode_ending(self): def barcode_ending(self):
super().barcode_ending() super().barcode_ending()
self.stock_request_id = self.env['stock.request'].create( self.stock_request_id = self.env["stock.request"].create(
self.stock_request_kanban_values() self.stock_request_kanban_values()
) )
self.status_state = 0
self.status = _('Added kanban %s for product %s' % (
self.stock_request_id.kanban_id.name,
self.stock_request_id.product_id.display_name
))
self.stock_request_ending() self.stock_request_ending()
self.update_status()
def stock_request_ending(self): def stock_request_ending(self):
self.stock_request_id.action_confirm() self.stock_request_id.action_confirm()
def update_status(self):
self.update(
{
"status_state": 0,
"status": _(
"Added kanban %s for product %s"
% (
self.stock_request_id.kanban_id.name,
self.stock_request_id.product_id.display_name,
)
),
}
)
def stock_request_kanban_values(self): def stock_request_kanban_values(self):
return { return {
'company_id': self.kanban_id.company_id.id, "company_id": self.kanban_id.company_id.id,
'procurement_group_id': "procurement_group_id": self.kanban_id.procurement_group_id.id or False,
self.kanban_id.procurement_group_id.id or False, "location_id": self.kanban_id.location_id.id or False,
'location_id': self.kanban_id.location_id.id or False, "warehouse_id": self.kanban_id.warehouse_id.id or False,
'warehouse_id': self.kanban_id.warehouse_id.id or False, "product_id": self.kanban_id.product_id.id,
'product_id': self.kanban_id.product_id.id, "product_uom_id": self.kanban_id.product_uom_id.id or False,
'product_uom_id': self.kanban_id.product_uom_id.id or False, "route_id": self.kanban_id.route_id.id or False,
'route_id': self.kanban_id.route_id.id or False, "product_uom_qty": self.kanban_id.product_uom_qty,
'product_uom_qty': self.kanban_id.product_uom_qty, "kanban_id": self.kanban_id.id,
'kanban_id': self.kanban_id.id,
} }

View File

@@ -1,47 +1,44 @@
# Copyright 2017 Creu Blanca # Copyright 2017 Creu Blanca
# Copyright 2017 Eficent Business and IT Consulting Services, S.L. # Copyright 2017-2020 ForgeFlow, S.L.
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html). # License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
from odoo import fields, models, _ from odoo import _, fields, models
class WizardStockRequestOrderKanbanAbstract(models.AbstractModel): class WizardStockRequestKanbanAbstract(models.AbstractModel):
_name = "wizard.stock.request.kanban.abstract" _name = "wizard.stock.request.kanban.abstract"
_description = "Stock Request Kanban Abstract Wizard"
_inherit = "barcodes.barcode_events_mixin" _inherit = "barcodes.barcode_events_mixin"
kanban_id = fields.Many2one( kanban_id = fields.Many2one("stock.request.kanban", readonly=True)
'stock.request.kanban', status = fields.Text(readonly=True, default="Start scanning")
readonly=True, status_state = fields.Integer(default=0, readonly=True)
)
status = fields.Text(
readonly=True,
default="Start scanning",
)
status_state = fields.Integer(
default=0,
readonly=True,
)
def on_barcode_scanned(self, barcode): def on_barcode_scanned(self, barcode):
self.kanban_id = self.env['stock.request.kanban'].search_barcode( self.kanban_id = self.env["stock.request.kanban"].search_barcode(barcode)
barcode)
if not self.kanban_id: if not self.kanban_id:
self.status = _("Barcode %s does not correspond to any " self.status = (
"Kanban. Try with another barcode or " _(
"press Close to finish scanning.") % barcode "Barcode %s does not correspond to any "
"Kanban. Try with another barcode or "
"press Close to finish scanning."
)
% barcode
)
self.status_state = 1 self.status_state = 1
return return
if self.validate_kanban(barcode): if self.validate_kanban(barcode):
self.status_state = 0 self.status_state = 0
self.barcode_ending() self.barcode_ending()
return
def barcode_ending(self): def barcode_ending(self):
pass pass
def validate_kanban(self, barcode): def validate_kanban(self, barcode):
''' """
It must return True if the kanban is valid, False otherwise It must return True if the kanban is valid, False otherwise
:param barcode: :param barcode:
:return: :return:
''' """
return True return True

View File

@@ -1,50 +1,55 @@
<?xml version="1.0"?> <?xml version="1.0" ?>
<!-- <!--
Copyright 2017 Eficent Business and IT Consulting Services, S.L. Copyright 2017-2020 ForgeFlow, S.L.
Copyright 2017 Creu Blanca Copyright 2017 Creu Blanca
License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html). License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
--> -->
<odoo> <odoo>
<record id="wizard_stock_request_kanban_form" model="ir.ui.view"> <record id="wizard_stock_request_kanban_form" model="ir.ui.view">
<field name="name">wizard.stock.request.kanban.form</field> <field name="name">wizard.stock.request.kanban.form</field>
<field name="model">wizard.stock.request.kanban</field> <field name="model">wizard.stock.request.kanban</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<form string="Add Kanban"> <form string="Add Kanban">
<div class="alert alert-success text-center o_form_header" <div
role="alert" style="margin-bottom:0px;" class="alert alert-success text-center o_form_header"
attrs="{'invisible':[('status_state', '!=', 0)]}"> role="alert"
<bold><field name="status"/></bold> style="margin-bottom:0px;"
attrs="{'invisible':[('status_state', '!=', 0)]}"
>
<bold>
<field name="status" />
</bold>
</div> </div>
<div class="alert alert-danger alert-dismissable text-center" <div
role="alert" style="margin-bottom:0px;" class="alert alert-danger alert-dismissable text-center"
attrs="{'invisible':[('status_state', '=', 0)]}"> role="alert"
<bold><field name="status"/></bold> style="margin-bottom:0px;"
attrs="{'invisible':[('status_state', '=', 0)]}"
>
<bold>
<field name="status" />
</bold>
</div> </div>
<field name="status_state" invisible="1"/> <field name="status_state" invisible="1" />
<field name="kanban_id" invisible="1"/> <field name="kanban_id" invisible="1" />
<field name="stock_request_id" invisible="1"/> <field name="stock_request_id" invisible="1" />
<field name="_barcode_scanned" widget="barcode_handler" <field name="_barcode_scanned" widget="barcode_handler" />
invisible="1"/>
<footer> <footer>
<button <button
name="action_cancel" name="action_cancel"
string="Close" string="Close"
class="oe_link" class="oe_link"
special="cancel" special="cancel"
/> />
</footer> </footer>
</form> </form>
</field> </field>
</record> </record>
<act_window
<act_window id="wizard_stock_request_kanban_action" id="wizard_stock_request_kanban_action"
name="Add Kanban" name="Add Kanban"
res_model="wizard.stock.request.kanban" res_model="wizard.stock.request.kanban"
view_mode="form" view_mode="form"
view_type="form" target="new"
target="new"/> />
</odoo> </odoo>

View File

@@ -1,19 +1,17 @@
# Copyright 2017 Creu Blanca # Copyright 2017 Creu Blanca
# Copyright 2017 Eficent Business and IT Consulting Services, S.L. # Copyright 2017-2020 ForgeFlow, S.L.
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html). # License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
from odoo import fields, models, _ from odoo import _, fields, models
from odoo.exceptions import ValidationError from odoo.exceptions import ValidationError
class WizardStockRequestOrderKanban(models.TransientModel): class WizardStockRequestOrderKanban(models.TransientModel):
_name = "wizard.stock.request.order.kanban" _name = "wizard.stock.request.order.kanban"
_description = "Stock Request Order Kanban Wizard"
_inherit = "wizard.stock.request.kanban" _inherit = "wizard.stock.request.kanban"
order_id = fields.Many2one( order_id = fields.Many2one("stock.request.order", required=True)
'stock.request.order',
required=True
)
def validate_kanban(self, barcode): def validate_kanban(self, barcode):
res = super().validate_kanban(barcode) res = super().validate_kanban(barcode)
@@ -23,30 +21,30 @@ class WizardStockRequestOrderKanban(models.TransientModel):
self.status = _("Barcode %s is on the order") % barcode self.status = _("Barcode %s is on the order") % barcode
self.status_state = 1 self.status_state = 1
return False return False
if self.order_id.state != 'draft': if self.order_id.state != "draft":
raise ValidationError(_( raise ValidationError(
'Lines only can be added on orders with draft state')) _("Lines only can be added on orders with draft state")
)
if not self.order_id.company_id: if not self.order_id.company_id:
self.order_id.company_id = self.kanban_id.company_id self.order_id.company_id = self.kanban_id.company_id
elif self.order_id.company_id != self.kanban_id.company_id: elif self.order_id.company_id != self.kanban_id.company_id:
raise ValidationError(_('Company must be the same')) raise ValidationError(_("Company must be the same"))
if ( if (
self.kanban_id.procurement_group_id and self.kanban_id.procurement_group_id
self.order_id.procurement_group_id != and self.order_id.procurement_group_id
self.kanban_id.procurement_group_id != self.kanban_id.procurement_group_id
): ):
raise ValidationError(_('Procurement group must be the same')) raise ValidationError(_("Procurement group must be the same"))
if self.order_id.location_id != self.kanban_id.location_id: if self.order_id.location_id != self.kanban_id.location_id:
raise ValidationError(_('Location must be the same')) raise ValidationError(_("Location must be the same"))
if self.order_id.warehouse_id != self.kanban_id.warehouse_id: if self.order_id.warehouse_id != self.kanban_id.warehouse_id:
raise ValidationError(_('Warehouse must be the same')) raise ValidationError(_("Warehouse must be the same"))
return res return res
def stock_request_kanban_values(self): def stock_request_kanban_values(self):
res = super().stock_request_kanban_values() res = super().stock_request_kanban_values()
res['order_id'] = self.order_id.id, res["order_id"] = (self.order_id.id,)
res['expected_date'] = \ res["expected_date"] = fields.Datetime.to_string(self.order_id.expected_date)
fields.Datetime.to_string(self.order_id.expected_date),
return res return res
def stock_request_ending(self): def stock_request_ending(self):

View File

@@ -1,31 +1,27 @@
<?xml version="1.0"?> <?xml version="1.0" ?>
<!-- <!--
Copyright 2017 Eficent Business and IT Consulting Services, S.L. Copyright 2017-2020 ForgeFlow, S.L.
Copyright 2017 Creu Blanca Copyright 2017 Creu Blanca
License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html). License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
--> -->
<odoo> <odoo>
<record id="wizard_stock_request_order_kanban_form" model="ir.ui.view"> <record id="wizard_stock_request_order_kanban_form" model="ir.ui.view">
<field name="name">wizard.stock.request.order.kanban.form</field> <field name="name">wizard.stock.request.order.kanban.form</field>
<field name="model">wizard.stock.request.order.kanban</field> <field name="model">wizard.stock.request.order.kanban</field>
<field name="mode">primary</field> <field name="mode">primary</field>
<field name="inherit_id" ref="wizard_stock_request_kanban_form"/> <field name="inherit_id" ref="wizard_stock_request_kanban_form" />
<field name="arch" type="xml"> <field name="arch" type="xml">
<field name="kanban_id" position="after"> <field name="kanban_id" position="after">
<field name="order_id" invisible="1"/> <field name="order_id" invisible="1" />
</field> </field>
</field> </field>
</record> </record>
<act_window
<act_window id="wizard_stock_request_order_kanban_action" id="wizard_stock_request_order_kanban_action"
name="Add Kanban" name="Add Kanban"
res_model="wizard.stock.request.order.kanban" res_model="wizard.stock.request.order.kanban"
view_mode="form" view_mode="form"
view_type="form" context="{'default_order_id': active_id}"
context="{'default_order_id': active_id}" target="new"
target="new"/> />
</odoo> </odoo>