summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnkita Raval <anr@odoo.com>2019-12-10 13:15:08 +0000
committersvs-odoo <svs@odoo.com>2020-03-04 10:12:46 +0000
commit65513ef3f92d82806d03bd5fe55c7f9af4f1484d (patch)
treebb8b71b6d1c19713abd5e81729459672ae0f2131
parenta56a4f8aeeebf946745b09b0b1a649293b806546 (diff)
[IMP] mrp: mrp_document
Purpose: Modernizes and facilitates files upload in manufacturing app. In this Commit: - Show the image in Kanban. - Allow to access a preview of the file when clicking on it in the Kanban view. - Add a 'Edit', 'Delete' and 'Download' in kanban card dropdown menu. - Add Upload button in control panel to upload document which will create new record of `mrp.document` and also add a testcase for it. task-1960733 closes odoo/odoo#42168 Related: odoo/enterprise#8977 Signed-off-by: Simon Lejeune (sle) <sle@openerp.com>
-rw-r--r--addons/mrp/__init__.py1
-rw-r--r--addons/mrp/__manifest__.py2
-rw-r--r--addons/mrp/controller/__init__.py4
-rw-r--r--addons/mrp/controller/main.py36
-rw-r--r--addons/mrp/static/src/js/mrp_document_kanban_view.js20
-rw-r--r--addons/mrp/static/src/js/mrp_documents_controller_mixin.js126
-rw-r--r--addons/mrp/static/src/js/mrp_documents_document_viewer.js19
-rw-r--r--addons/mrp/static/src/js/mrp_documents_kanban_controller.js37
-rw-r--r--addons/mrp/static/src/js/mrp_documents_kanban_record.js50
-rw-r--r--addons/mrp/static/src/js/mrp_documents_kanban_renderer.js27
-rw-r--r--addons/mrp/static/src/scss/mrp_document_kanban_view.scss3
-rw-r--r--addons/mrp/static/src/xml/mrp_document_template.xml10
-rw-r--r--addons/mrp/static/tests/mrp_document_kanban_tests.js172
-rw-r--r--addons/mrp/views/ir_attachment_view.xml53
-rw-r--r--addons/mrp/views/mrp_templates.xml8
15 files changed, 556 insertions, 12 deletions
diff --git a/addons/mrp/__init__.py b/addons/mrp/__init__.py
index 7a2198e463d..616729a420d 100644
--- a/addons/mrp/__init__.py
+++ b/addons/mrp/__init__.py
@@ -4,6 +4,7 @@
from . import models
from . import wizard
from . import report
+from . import controller
from odoo import api, SUPERUSER_ID
diff --git a/addons/mrp/__manifest__.py b/addons/mrp/__manifest__.py
index 09f62e9d37c..f664c4883e2 100644
--- a/addons/mrp/__manifest__.py
+++ b/addons/mrp/__manifest__.py
@@ -40,7 +40,7 @@
'report/report_stock_rule.xml',
'report/mrp_zebra_production_templates.xml',
],
- 'qweb': ['static/src/xml/mrp.xml'],
+ 'qweb': ['static/src/xml/*.xml'],
'demo': [
'data/mrp_demo.xml',
],
diff --git a/addons/mrp/controller/__init__.py b/addons/mrp/controller/__init__.py
new file mode 100644
index 00000000000..5d4b25db9c0
--- /dev/null
+++ b/addons/mrp/controller/__init__.py
@@ -0,0 +1,4 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from . import main
diff --git a/addons/mrp/controller/main.py b/addons/mrp/controller/main.py
new file mode 100644
index 00000000000..0c8b40f0621
--- /dev/null
+++ b/addons/mrp/controller/main.py
@@ -0,0 +1,36 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+import base64
+import json
+import logging
+
+from odoo import http
+from odoo.http import request
+from odoo.tools.mimetypes import neuter_mimetype
+from odoo.tools.translate import _
+
+logger = logging.getLogger(__name__)
+
+
+class MrpDocumentRoute(http.Controller):
+
+ @http.route('/mrp/upload_attachment', type='http', methods=['POST'], auth="user")
+ def upload_document(self, ufile, **kwargs):
+ files = request.httprequest.files.getlist('ufile')
+ result = {'success': _("All files uploaded")}
+ for ufile in files:
+ try:
+ mimetype = neuter_mimetype(ufile.content_type, http.request.env.user)
+ request.env['mrp.document'].create({
+ 'name': ufile.filename,
+ 'res_model': kwargs.get('res_model'),
+ 'res_id': kwargs.get('res_id'),
+ 'mimetype': mimetype,
+ 'datas': base64.encodebytes(ufile.read()),
+ })
+ except Exception as e:
+ logger.exception("Fail to upload document %s" % ufile.filename)
+ result = {'error': str(e)}
+
+ return json.dumps(result)
diff --git a/addons/mrp/static/src/js/mrp_document_kanban_view.js b/addons/mrp/static/src/js/mrp_document_kanban_view.js
new file mode 100644
index 00000000000..e4e936637b1
--- /dev/null
+++ b/addons/mrp/static/src/js/mrp_document_kanban_view.js
@@ -0,0 +1,20 @@
+odoo.define('mrp.MrpDocumentsKanbanView', function (require) {
+"use strict";
+
+const KanbanView = require('web.KanbanView');
+const MrpDocumentsKanbanController = require('mrp.MrpDocumentsKanbanController');
+const MrpDocumentsKanbanRenderer = require('mrp.MrpDocumentsKanbanRenderer');
+const viewRegistry = require('web.view_registry');
+
+const MrpDocumentsKanbanView = KanbanView.extend({
+ config: Object.assign({}, KanbanView.prototype.config, {
+ Controller: MrpDocumentsKanbanController,
+ Renderer: MrpDocumentsKanbanRenderer,
+ }),
+});
+
+viewRegistry.add('mrp_documents_kanban', MrpDocumentsKanbanView);
+
+return MrpDocumentsKanbanView;
+
+});
diff --git a/addons/mrp/static/src/js/mrp_documents_controller_mixin.js b/addons/mrp/static/src/js/mrp_documents_controller_mixin.js
new file mode 100644
index 00000000000..9bcaf3d6cf3
--- /dev/null
+++ b/addons/mrp/static/src/js/mrp_documents_controller_mixin.js
@@ -0,0 +1,126 @@
+odoo.define('mrp.controllerMixin', function (require) {
+'use strict';
+
+const { _t, qweb } = require('web.core');
+const fileUploadMixin = require('web.fileUploadMixin');
+const DocumentViewer = require('mrp.MrpDocumentViewer');
+
+const MrpDocumentsControllerMixin = Object.assign({}, fileUploadMixin, {
+ events: {
+ 'click .o_mrp_documents_kanban_upload': '_onClickMrpDocumentsUpload',
+ },
+ custom_events: Object.assign({}, fileUploadMixin.custom_events, {
+ kanban_image_clicked: '_onKanbanPreview',
+ upload_file: '_onUploadFile',
+ }),
+
+ //--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ /**
+ * Called right after the reload of the view.
+ */
+ async reload() {
+ await this._renderFileUploads();
+ },
+
+ /**
+ * @override
+ * @param {jQueryElement} $node
+ */
+ renderButtons($node) {
+ this.$buttons = $(qweb.render('MrpDocumentsKanbanView.buttons'));
+ this.$buttons.appendTo($node);
+ },
+
+ //--------------------------------------------------------------------------
+ // Private
+ //--------------------------------------------------------------------------
+
+ /**
+ * @override
+ */
+ _getFileUploadRoute() {
+ return '/mrp/upload_attachment';
+ },
+
+ /**
+ * @override
+ * @param {integer} param0.recordId
+ */
+ _makeFileUploadFormDataKeys() {
+ const context = this.model.get(this.handle, { raw: true }).getContext();
+ return {
+ res_id: context.default_res_id,
+ res_model: context.default_res_model,
+ };
+ },
+
+ //--------------------------------------------------------------------------
+ // Handlers
+ //--------------------------------------------------------------------------
+
+ /**
+ * @private
+ */
+ _onClickMrpDocumentsUpload() {
+ const $uploadInput = $('<input>', {
+ type: 'file',
+ name: 'files[]',
+ multiple: 'multiple'
+ });
+ $uploadInput.on('change', async ev => {
+ await this._uploadFiles(ev.target.files);
+ $uploadInput.remove();
+ });
+ $uploadInput.click();
+ },
+
+ /**
+ * Handles custom event to display the document viewer.
+ *
+ * @private
+ * @param {OdooEvent} ev
+ * @param {integer} ev.data.recordID
+ * @param {Array<Object>} ev.data.recordList
+ */
+ _onKanbanPreview(ev) {
+ ev.stopPropagation();
+ const documents = ev.data.recordList;
+ const documentID = ev.data.recordID;
+ const documentViewer = new DocumentViewer(this, documents, documentID);
+ documentViewer.appendTo(this.$('.o_mrp_documents_kanban_view'));
+ },
+
+ /**
+ * Specially created to call `_uploadFiles` method from tests.
+ *
+ * @private
+ * @param {OdooEvent} ev
+ */
+ async _onUploadFile(ev) {
+ await this._uploadFiles(ev.data.files);
+ },
+
+ /**
+ * @override
+ * @param {Object} param0
+ * @param {XMLHttpRequest} param0.xhr
+ */
+ _onUploadLoad({ xhr }) {
+ const result = xhr.status === 200
+ ? JSON.parse(xhr.response)
+ : {
+ error: _.str.sprintf(_t("status code: %s </br> message: %s"), xhr.status, xhr.response)
+ };
+ if (result.error) {
+ this.do_notify(_t("Error"), result.error, true);
+ }
+ fileUploadMixin._onUploadLoad.apply(this, arguments);
+ },
+});
+
+return MrpDocumentsControllerMixin;
+
+});
diff --git a/addons/mrp/static/src/js/mrp_documents_document_viewer.js b/addons/mrp/static/src/js/mrp_documents_document_viewer.js
new file mode 100644
index 00000000000..c0d7f3e8c8d
--- /dev/null
+++ b/addons/mrp/static/src/js/mrp_documents_document_viewer.js
@@ -0,0 +1,19 @@
+odoo.define('mrp.MrpDocumentViewer', function (require) {
+"use strict";
+
+const DocumentViewer = require('mail.DocumentViewer');
+
+/**
+ * This file defines the DocumentViewer for the MRP Documents Kanban view.
+ */
+const MrpDocumentsDocumentViewer = DocumentViewer.extend({
+ init(parent, attachments, activeAttachmentID) {
+ this._super(...arguments);
+ this.modelName = 'mrp.document';
+ },
+});
+
+return MrpDocumentsDocumentViewer;
+
+});
+
diff --git a/addons/mrp/static/src/js/mrp_documents_kanban_controller.js b/addons/mrp/static/src/js/mrp_documents_kanban_controller.js
new file mode 100644
index 00000000000..9a123dfb970
--- /dev/null
+++ b/addons/mrp/static/src/js/mrp_documents_kanban_controller.js
@@ -0,0 +1,37 @@
+odoo.define('mrp.MrpDocumentsKanbanController', function (require) {
+"use strict";
+
+/**
+ * This file defines the Controller for the MRP Documents Kanban view, which is an
+ * override of the KanbanController.
+ */
+
+const MrpDocumentsControllerMixin = require('mrp.controllerMixin');
+
+const KanbanController = require('web.KanbanController');
+
+const MrpDocumentsKanbanController = KanbanController.extend(MrpDocumentsControllerMixin, {
+ events: Object.assign({}, KanbanController.prototype.events, MrpDocumentsControllerMixin.events),
+ custom_events: Object.assign({}, KanbanController.prototype.custom_events, MrpDocumentsControllerMixin.custom_events),
+
+ /**
+ * @override
+ */
+ init() {
+ this._super(...arguments);
+ MrpDocumentsControllerMixin.init.apply(this, arguments);
+ },
+ /**
+ * Override to update the records selection.
+ *
+ * @override
+ */
+ async reload() {
+ await this._super(...arguments);
+ await MrpDocumentsControllerMixin.reload.apply(this, arguments);
+ },
+});
+
+return MrpDocumentsKanbanController;
+
+});
diff --git a/addons/mrp/static/src/js/mrp_documents_kanban_record.js b/addons/mrp/static/src/js/mrp_documents_kanban_record.js
new file mode 100644
index 00000000000..445588341ee
--- /dev/null
+++ b/addons/mrp/static/src/js/mrp_documents_kanban_record.js
@@ -0,0 +1,50 @@
+odoo.define('mrp.MrpDocumentsKanbanRecord', function (require) {
+"use strict";
+
+/**
+ * This file defines the KanbanRecord for the MRP Documents Kanban view.
+ */
+
+const KanbanRecord = require('web.KanbanRecord');
+
+const MrpDocumentsKanbanRecord = KanbanRecord.extend({
+ events: Object.assign({}, KanbanRecord.prototype.events, {
+ 'click .o_mrp_download': '_onDownload',
+ 'click .o_kanban_previewer': '_onImageClicked',
+ }),
+
+ //--------------------------------------------------------------------------
+ // Handlers
+ //--------------------------------------------------------------------------
+
+ /**
+ * Handles the click on the download link to save the attachment locally.
+ *
+ * @private
+ * @param {MouseEvent} ev
+ */
+ _onDownload(ev) {
+ ev.preventDefault();
+ window.location = `/web/content/${this.modelName}/${this.id}/datas?download=true`;
+ },
+
+ /**
+ * Handles the click on the preview image. Triggers up `_onKanbanPreview` to
+ * display `DocumentViewer`.
+ *
+ * @private
+ * @param {MouseEvent} ev
+ */
+ _onImageClicked(ev) {
+ ev.preventDefault();
+ ev.stopPropagation();
+ this.trigger_up('kanban_image_clicked', {
+ recordList: [this.recordData],
+ recordID: this.recordData.id
+ });
+ },
+});
+
+return MrpDocumentsKanbanRecord;
+
+});
diff --git a/addons/mrp/static/src/js/mrp_documents_kanban_renderer.js b/addons/mrp/static/src/js/mrp_documents_kanban_renderer.js
new file mode 100644
index 00000000000..79ffffe7aee
--- /dev/null
+++ b/addons/mrp/static/src/js/mrp_documents_kanban_renderer.js
@@ -0,0 +1,27 @@
+odoo.define('mrp.MrpDocumentsKanbanRenderer', function (require) {
+"use strict";
+
+/**
+ * This file defines the Renderer for the MRP Documents Kanban view, which is an
+ * override of the KanbanRenderer.
+ */
+
+const KanbanRenderer = require('web.KanbanRenderer');
+const MrpDocumentsKanbanRecord = require('mrp.MrpDocumentsKanbanRecord');
+
+const MrpDocumentsKanbanRenderer = KanbanRenderer.extend({
+ config: Object.assign({}, KanbanRenderer.prototype.config, {
+ KanbanRecord: MrpDocumentsKanbanRecord,
+ }),
+ /**
+ * @override
+ */
+ async start() {
+ this.$el.addClass('o_mrp_documents_kanban_view');
+ await this._super(...arguments);
+ },
+});
+
+return MrpDocumentsKanbanRenderer;
+
+});
diff --git a/addons/mrp/static/src/scss/mrp_document_kanban_view.scss b/addons/mrp/static/src/scss/mrp_document_kanban_view.scss
new file mode 100644
index 00000000000..536ef77d028
--- /dev/null
+++ b/addons/mrp/static/src/scss/mrp_document_kanban_view.scss
@@ -0,0 +1,3 @@
+.o_kanban_previewer:hover {
+ cursor: zoom-in;
+}
diff --git a/addons/mrp/static/src/xml/mrp_document_template.xml b/addons/mrp/static/src/xml/mrp_document_template.xml
new file mode 100644
index 00000000000..23ea08d860e
--- /dev/null
+++ b/addons/mrp/static/src/xml/mrp_document_template.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<templates xml:space="preserve">
+
+<div t-name="MrpDocumentsKanbanView.buttons">
+ <button type="button" t-attf-class="btn btn-primary o_mrp_documents_kanban_upload">
+ Upload
+ </button>
+</div>
+</templates>
+
diff --git a/addons/mrp/static/tests/mrp_document_kanban_tests.js b/addons/mrp/static/tests/mrp_document_kanban_tests.js
new file mode 100644
index 00000000000..366bb1bd1da
--- /dev/null
+++ b/addons/mrp/static/tests/mrp_document_kanban_tests.js
@@ -0,0 +1,172 @@
+odoo.define('mrp.document_kanban_tests', function (require) {
+"use strict";
+
+const MrpDocumentsKanbanView = require('mrp.MrpDocumentsKanbanView');
+const MrpDocumentsKanbanController = require('mrp.MrpDocumentsKanbanController');
+const testUtils = require('web.test_utils');
+
+const createView = testUtils.createView;
+
+QUnit.module('Views', {}, function () {
+
+QUnit.module('MrpDocumentsKanbanView', {
+ beforeEach: function () {
+ this.ORIGINAL_CREATE_XHR = MrpDocumentsKanbanController.prototype._createXHR;
+ this.patchDocumentXHR = (mockedXHRs, customSend) => {
+ MrpDocumentsKanbanController.prototype._createXhr = () => {
+ const xhr = {
+ upload: new window.EventTarget(),
+ open() { },
+ send(data) { customSend && customSend(data); },
+ };
+ mockedXHRs.push(xhr);
+ return xhr;
+ };
+ };
+ this.data = {
+ 'mrp.document': {
+ fields: {
+ name: {string: "Name", type: 'char', default: ' '},
+ priority: {string: 'priority', type: 'selection',
+ selection: [['0', 'Normal'], ['1', 'Low'], ['2', 'High'], ['3', 'Very High']]},
+ },
+ records: [
+ {id: 1, name: 'test1', priority: 2},
+ {id: 4, name: 'test2', priority: 1},
+ {id: 3, name: 'test3', priority: 3},
+ ],
+ },
+ };
+ },
+ afterEach() {
+ MrpDocumentsKanbanController.prototype._createXHR = this.ORIGINAL_CREATE_XHR;
+ },
+}, function () {
+ QUnit.test('MRP documents kanban basic rendering', async function (assert) {
+ assert.expect(6);
+
+ const kanban = await createView({
+ View: MrpDocumentsKanbanView,
+ model: 'mrp.document',
+ data: this.data,
+ arch: '<kanban><templates><t t-name="kanban-box">' +
+ '<div>' +
+ '<field name="name"/>' +
+ '</div>' +
+ '</t></templates></kanban>',
+ });
+
+ assert.ok(kanban, "kanban is created");
+ assert.ok(kanban.$buttons.find('.o_mrp_documents_kanban_upload'),
+ "should have upload button in kanban buttons");
+ assert.containsN(kanban, '.o_kanban_view .o_kanban_record:not(.o_kanban_ghost)', 3,
+ "should have 3 records in the renderer");
+ // check view layout
+ assert.hasClass(kanban.$('.o_kanban_view'), 'o_mrp_documents_kanban_view',
+ "should have classname 'o_mrp_documents_kanban_view'");
+ // check control panel buttons
+ assert.containsN(kanban, '.o_cp_buttons .btn-primary', 1,
+ "should have only 1 primary button i.e. Upload button");
+ assert.strictEqual(kanban.$('.o_cp_buttons .btn-primary:first').text().trim(), 'Upload',
+ "should have a primary 'Upload' button");
+
+ kanban.destroy();
+ });
+
+ QUnit.test('mrp: upload multiple files', async function (assert) {
+ assert.expect(4);
+
+ const file1 = await testUtils.file.createFile({
+ name: 'text1.txt',
+ content: 'hello, world',
+ contentType: 'text/plain',
+ });
+ const file2 = await testUtils.file.createFile({
+ name: 'text2.txt',
+ content: 'hello, world',
+ contentType: 'text/plain',
+ });
+ const file3 = await testUtils.file.createFile({
+ name: 'text3.txt',
+ content: 'hello, world',
+ contentType: 'text/plain',
+ });
+
+ const mockedXHRs = [];
+ this.patchDocumentXHR(mockedXHRs, data => assert.step('xhrSend'));
+
+ const kanban = await createView({
+ View: MrpDocumentsKanbanView,
+ model: 'mrp.document',
+ data: this.data,
+ arch: '<kanban><templates><t t-name="kanban-box">' +
+ '<div>' +
+ '<field name="name"/>' +
+ '</div>' +
+ '</t></templates></kanban>',
+ });
+
+ kanban.trigger_up('upload_file', {files: [file1]});
+ await testUtils.nextTick();
+ assert.verifySteps(['xhrSend']);
+
+ kanban.trigger_up('upload_file', {files: [file2, file3]});
+ await testUtils.nextTick();
+ assert.verifySteps(['xhrSend']);
+
+ kanban.destroy();
+ });
+
+ QUnit.test('mrp: upload progress bars', async function (assert) {
+ assert.expect(4);
+
+ const file1 = await testUtils.file.createFile({
+ name: 'text1.txt',
+ content: 'hello, world',
+ contentType: 'text/plain',
+ });
+
+ const mockedXHRs = [];
+ this.patchDocumentXHR(mockedXHRs, data => assert.step('xhrSend'));
+
+ const kanban = await createView({
+ View: MrpDocumentsKanbanView,
+ model: 'mrp.document',
+ data: this.data,
+ arch: '<kanban><templates><t t-name="kanban-box">' +
+ '<div>' +
+ '<field name="name"/>' +
+ '</div>' +
+ '</t></templates></kanban>',
+ });
+
+ kanban.trigger_up('upload_file', {files: [file1]});
+ await testUtils.nextTick();
+ assert.verifySteps(['xhrSend']);
+
+ const progressEvent = new Event('progress', { bubbles: true });
+ progressEvent.loaded = 250000000;
+ progressEvent.total = 500000000;
+ progressEvent.lengthComputable = true;
+ mockedXHRs[0].upload.dispatchEvent(progressEvent);
+ assert.strictEqual(
+ kanban.$('.o_file_upload_progress_text_left').text(),
+ "Uploading... (50%)",
+ "the current upload progress should be at 50%"
+ );
+
+ progressEvent.loaded = 350000000;
+ mockedXHRs[0].upload.dispatchEvent(progressEvent);
+ assert.strictEqual(
+ kanban.$('.o_file_upload_progress_text_right').text(),
+ "(350/500Mb)",
+ "the current upload progress should be at (350/500Mb)"
+ );
+
+ kanban.destroy();
+ });
+});
+
+});
+
+});
diff --git a/addons/mrp/views/ir_attachment_view.xml b/addons/mrp/views/ir_attachment_view.xml
index b187ebb8196..6e144e98465 100644
--- a/addons/mrp/views/ir_attachment_view.xml
+++ b/addons/mrp/views/ir_attachment_view.xml
@@ -5,7 +5,7 @@
<field name="name">mrp.document kanban.mrp</field>
<field name="model">mrp.document</field>
<field name="arch" type="xml">
- <kanban>
+ <kanban js_class="mrp_documents_kanban" create="false">
<field name="ir_attachment_id"/>
<field name="mimetype"/>
<field name="type"/>
@@ -16,11 +16,13 @@
<t t-name="kanban-box">
<div class="oe_kanban_global_area o_kanban_attachment oe_kanban_global_click">
<div class="o_kanban_image">
- <div class="o_kanban_image_wrapper">
+ <t t-set="binaryPreviewable"
+ t-value="new RegExp('(image|video|application/pdf|text)').test(record.mimetype.value) &amp;&amp; record.type.raw_value === 'binary'"/>
+ <div t-attf-class="o_kanban_image_wrapper #{(webimage or binaryPreviewable) ? 'o_kanban_previewer' : ''}">
<t t-set="webimage" t-value="new RegExp('image.*(gif|jpeg|jpg|png)').test(record.mimetype.value)"/>
<div t-if="record.type.raw_value == 'url'" class="o_url_image fa fa-link fa-3x text-muted" aria-label="Image is a link"/>
<img t-elif="webimage" t-attf-src="/web/image/#{record.ir_attachment_id.raw_value}" width="100" height="100" alt="Document" class="o_attachment_image"/>
- <div t-else="!webimage" class="o_image o_image_thumbnail" t-att-data-mimetype="record.mimetype.value"/>
+ <div t-else="" class="o_image o_image_thumbnail" t-att-data-mimetype="record.mimetype.value"/>
</div>
</div>
<div class="o_kanban_details">
@@ -29,7 +31,6 @@
<field name="name" class="o_text_overflow"/>
</div>
<div class="o_kanban_record_body">
- <field name="name" attrs="{'invisible':[('type','=','url')]}"/>
<field name="url" widget="url" attrs="{'invisible':[('type','=','binary')]}"/>
</div>
<div class="o_kanban_record_bottom">
@@ -40,6 +41,16 @@
<img t-att-src="kanban_image('res.users', 'image_128', record.create_uid.raw_value)" t-att-data-member_id="record.create_uid.raw_value" t-att-alt="record.create_uid.raw_value" class="oe_kanban_avatar"/>
</div>
</div>
+ <div class="o_dropdown_kanban dropdown" tabindex="-1">
+ <a class="dropdown-toggle o-no-caret btn" data-toggle="dropdown" href="#" role="button" aria-label="Dropdown menu" title="Dropdown menu">
+ <span class="fa fa-ellipsis-v"/>
+ </a>
+ <div class="dropdown-menu" role="menu" aria-labelledby="dLabel">
+ <a t-if="widget.editable" type="edit" class="dropdown-item">Edit</a>
+ <a t-if="widget.deletable" type="delete" class="dropdown-item">Delete</a>
+ <a class="dropdown-item o_mrp_download">Download</a>
+ </div>
+ </div>
</div>
</div>
</div>
@@ -49,16 +60,36 @@
</field>
</record>
- <record id="view_document_form" model="ir.ui.view">
+ <record id="view_mrp_document_form" model="ir.ui.view">
<field name="name">mrp.document.form</field>
<field name="model">mrp.document</field>
- <field name="inherit_id" ref="base.view_attachment_form"/>
- <field name="mode">primary</field>
<field name="arch" type="xml">
- <xpath expr="//field[@name='mimetype']" position="after">
- <field name="priority" widget="priority"/>
- <field name="company_id" groups="base.group_multi_company"/>
- </xpath>
+ <form string="Attachments">
+ <sheet>
+ <label for="name" class="oe_edit_only"/>
+ <h1>
+ <field name="name"/>
+ </h1>
+ <group>
+ <group>
+ <field name="type"/>
+ <field name="datas" filename="name" attrs="{'invisible':[('type','=','url')]}"/>
+ <field name="url" widget="url" attrs="{'invisible':[('type','=','binary')]}"/>
+ </group>
+ <group string="Attached To" groups="base.group_no_one">
+ <field name="res_name"/>
+ <field name="company_id" groups="base.group_multi_company" options="{'no_create': True}"/>
+ </group>
+ <group string="History" groups="base.group_no_one" attrs="{'invisible':[('create_date','=',False)]}">
+ <label for="create_uid" string="Creation"/>
+ <div name="creation_div">
+ <field name="create_uid" readonly="1" class="oe_inline"/> on
+ <field name="create_date" readonly="1" class="oe_inline"/>
+ </div>
+ </group>
+ </group>
+ </sheet>
+ </form>
</field>
</record>
</odoo>
diff --git a/addons/mrp/views/mrp_templates.xml b/addons/mrp/views/mrp_templates.xml
index 9f2b80215ce..24b891d49bd 100644
--- a/addons/mrp/views/mrp_templates.xml
+++ b/addons/mrp/views/mrp_templates.xml
@@ -6,6 +6,12 @@
<script type="text/javascript" src="/mrp/static/src/js/mrp.js"></script>
<script type="text/javascript" src="/mrp/static/src/js/mrp_bom_report.js"></script>
<script type="text/javascript" src="/mrp/static/src/js/mrp_workorder_popover.js"></script>
+ <script type="text/javascript" src="/mrp/static/src/js/mrp_documents_controller_mixin.js"></script>
+ <script type="text/javascript" src="/mrp/static/src/js/mrp_documents_document_viewer.js"></script>
+ <script type="text/javascript" src="/mrp/static/src/js/mrp_documents_kanban_controller.js"></script>
+ <script type="text/javascript" src="/mrp/static/src/js/mrp_documents_kanban_record.js"></script>
+ <script type="text/javascript" src="/mrp/static/src/js/mrp_documents_kanban_renderer.js"></script>
+ <script type="text/javascript" src="/mrp/static/src/js/mrp_document_kanban_view.js"></script>
</xpath>
</template>
@@ -14,12 +20,14 @@
<link rel="stylesheet" type="text/scss" href="/mrp/static/src/scss/mrp_bom_report.scss" />
<link rel="stylesheet" type="text/scss" href="/mrp/static/src/scss/mrp_fields.scss" />
<link rel="stylesheet" type="text/scss" href="/mrp/static/src/scss/mrp_gantt.scss" />
+ <link rel="stylesheet" type="text/scss" href="/mrp/static/src/scss/mrp_document_kanban_view.scss" />
</xpath>
</template>
<template id="qunit_suite" inherit_id="web.qunit_suite">
<xpath expr="//script[last()]" position="after">
<script type="text/javascript" src="/mrp/static/tests/mrp_tests.js"/>
+ <script type="text/javascript" src="/mrp/static/tests/mrp_document_kanban_tests.js"/>
</xpath>
</template>