[IMP] account: bills upload wowl migration

Part-of: odoo/odoo#96672
This commit is contained in:
Habib (ayh)
2022-08-27 19:01:11 +02:00
parent b0ee5be676
commit db008ff03a
7 changed files with 436 additions and 179 deletions
+1
View File
@@ -107,6 +107,7 @@ You could use this simplified accounting in case you work with an (external) acc
'account/static/src/js/legacy_section_and_note_fields_backend.js',
'account/static/src/js/tours/account.js',
'account/static/src/js/bills_upload.js',
'account/static/src/js/legacy_bills_upload.js',
'account/static/src/js/account_selection.js',
'account/static/src/js/open_move_widget.js',
'account/static/src/js/account_move_form.js',
+145 -148
View File
@@ -1,160 +1,157 @@
odoo.define('account.upload.document', function (require) {
"use strict";
/** @odoo-module **/
var core = require('web.core');
var qweb = core.qweb;
var ListController = require('web.ListController');
var ListView = require('web.ListView');
var KanbanController = require('web.KanbanController');
var KanbanView = require('web.KanbanView');
var viewRegistry = require('web.view_registry');
import { registry } from "@web/core/registry";
import { useService } from "@web/core/utils/hooks";
import { listView } from "@web/views/list/list_view";
import { ListRenderer } from "@web/views/list/list_renderer";
import { ListController } from "@web/views/list/list_controller";
import { kanbanView } from "@web/views/kanban/kanban_view";
import { KanbanRenderer } from "@web/views/kanban/kanban_renderer";
import { KanbanController } from "@web/views/kanban/kanban_controller";
import { KanbanRecord } from "@web/views/kanban/kanban_record";
import { FileUploader } from "@web/views/fields/file_handler";
var UploadDocumentMixin = {
events: {
'click .o_button_upload_document': '_onUpload',
'change .o_account_document_upload .o_form_binary_form': '_onAddAttachment',
'drop .o_drop_area': '_onDrop',
'dragenter .o_content': '_highlight',
'dragleave .o_drop_area': '_unhighlight',
'dragover .o_drop_area': '_clear',
},
const { Component, useState } = owl;
start: async function () {
// define a unique uploadId and a callback method
this.fileUploadID = _.uniqueId('account_bill_file_upload');
$(window).on(this.fileUploadID, this._onFileUploaded.bind(this));
let result = await this._super.apply(this, arguments);
await this.$(this._dropZone).prepend($(qweb.render('account.DocumentDropZone')));
return result;
},
reload: async function () {
let result = await this._super.apply(this, arguments);
await this.$(this._dropZone).prepend($(qweb.render('account.DocumentDropZone')));
return result;
},
_onAddAttachment: function (ev) {
// Auto submit form once we've selected an attachment
var $input = $(ev.currentTarget).find('input.o_input_file');
if ($input.val() !== '') {
var $binaryForm = this.$('.o_account_document_upload form.o_form_binary_form');
$binaryForm.submit();
}
},
_onFileUploaded: function () {
// Callback once attachment have been created, create a document with attachment ids
var self = this;
var attachments = Array.prototype.slice.call(arguments, 1);
// Get id from result
var attachent_ids = attachments.reduce(function(filtered, record) {
if (record.id) {
filtered.push(record.id);
}
return filtered;
}, []);
return this._rpc({
model: 'account.journal',
method: 'create_document_from_attachment',
args: ["", attachent_ids],
context: this.initialState.context,
}).then(function(result) {
self.do_action(result);
}).catch(function () {
// Reset the file input, allowing to select again the same file if needed
self.$('.o_account_document_upload .o_input_file').val('');
});
},
_createForm: function() {
// If hidden upload form don't exists, create it
var $formContainer = this.$('.o_content').find('.o_account_document_upload');
if (!$formContainer.length) {
$formContainer = $(qweb.render('account.DocumentsHiddenUploadForm', {widget: this}));
$formContainer.appendTo(this.$('.o_content'));
}
},
_onUpload: function (ev) {
this._createForm();
// Trigger the input to select a file
this.$('.o_account_document_upload .o_input_file').click();
},
_highlight: function(ev) {
$('.o_drop_area').show();
},
_unhighlight: function(ev) {
$('.o_drop_area').hide();
},
_clear: function(ev) {
ev.preventDefault();
},
_onDrop: function (ev) {
ev.preventDefault();
this._createForm();
if (ev.originalEvent.dataTransfer.files.length) {
this.$('.o_account_document_upload .o_input_file')[0].files = ev.originalEvent.dataTransfer.files;
this.$('.o_form_binary_form')[0].submit();
}
this._unhighlight(ev);
},
export class AccountFileUploader extends Component {
setup() {
this.orm = useService("orm");
this.action = useService("action");
this.attachmentIdsToProcess = [];
}
var DocumentsListView = ListView.extend({
config: Object.assign({}, ListView.prototype.config, {
Controller: ListController.extend({_dropZone: '.o_content'}, UploadDocumentMixin, {
buttons_template: 'DocumentsListView.buttons',
events: Object.assign({}, ListController.prototype.events, UploadDocumentMixin.events),
}),
}),
});
async onFileUploaded(file) {
let att_data = {
name: file.name,
mimetype: file.type,
datas: file.data,
};
const att_id = await this.orm.create("ir.attachment", att_data, {
...this.props.extraContext, ...this.env.searchModel.context,
});
this.attachmentIdsToProcess.push(att_id);
}
var DocumentsKanbanView = KanbanView.extend({
config: Object.assign({}, KanbanView.prototype.config, {
Controller: KanbanController.extend({_dropZone: '.o_content'}, UploadDocumentMixin, {
buttons_template: 'DocumentsKanbanView.buttons',
events: Object.assign({}, KanbanController.prototype.events, UploadDocumentMixin.events),
}),
}),
});
async onUploadComplete() {
const action = await this.orm.call("account.journal", "create_document_from_attachment", ["", this.attachmentIdsToProcess], {
context: { ...this.props.extraContext, ...this.env.searchModel.context },
});
this.attachmentIdsToProcess = [];
this.action.doAction(action);
}
}
AccountFileUploader.components = {
FileUploader,
};
AccountFileUploader.template = "account.AccountFileUploader";
var DashboardKanbanController = KanbanController.extend({_dropZone: '.o_kanban_record'}, UploadDocumentMixin, {
events: Object.assign({}, KanbanController.prototype.events, UploadDocumentMixin.events),
export class AccountDropZone extends Component {
setup() {
this.notificationService = useService("notification");
}
_setJournalContext: function(ev) {
var kanbanRecord = $(ev.currentTarget).closest('.o_kanban_record').data('record');
this.initialState.context['default_journal_id'] = kanbanRecord.id;
let journal_type = kanbanRecord.state.data.type;
if (journal_type == 'sale') {
this.initialState.context['default_move_type'] = 'out_invoice';
} else if (journal_type == 'purchase') {
this.initialState.context['default_move_type'] = 'in_invoice';
}
},
onDrop(ev) {
const selector = '.account_file_uploader.o_input_file.o_hidden';
// look for the closest uploader Input as it may have a context
let uploadInput = ev.target.closest('.o_drop_area').parentElement.querySelector(selector) || document.querySelector(selector);
let files = ev.dataTransfer ? ev.dataTransfer.files : false;
if (uploadInput && !!files) {
uploadInput.files = ev.dataTransfer.files;
uploadInput.dispatchEvent(new Event("change"));
} else {
this.notificationService.add(
this.env._t("Could not upload files"),
{
type: "danger",
});
}
this.props.hideZone();
}
}
AccountDropZone.defaultProps = {
hideZone: () => {},
};
AccountDropZone.template = "account.DropZone";
_onUpload: function (ev) {
this._setJournalContext(ev);
UploadDocumentMixin._onUpload.apply(this, arguments);
},
// Account Move List View
export class AccountMoveUploadListRenderer extends ListRenderer {
setup() {
super.setup();
this.state.dropzoneVisible = false;
}
}
AccountMoveUploadListRenderer.template = "account.ListRenderer";
AccountMoveUploadListRenderer.components = {
...ListRenderer.components,
AccountDropZone,
};
_onDrop: function (ev) {
this._setJournalContext(ev);
UploadDocumentMixin._onDrop.apply(this, arguments);
},
});
export class AccountMoveUploadListController extends ListController {}
AccountMoveUploadListController.components = {
...ListController.components,
AccountFileUploader,
};
var DashboardKanbanView = KanbanView.extend({
config: Object.assign({}, KanbanView.prototype.config, {
Controller: DashboardKanbanController,
}),
});
export const AccountMoveUploadListView = {
...listView,
Controller: AccountMoveUploadListController,
Renderer: AccountMoveUploadListRenderer,
buttonTemplate: "account.ListView.Buttons",
};
viewRegistry.add('account_tree', DocumentsListView);
viewRegistry.add('account_documents_kanban', DocumentsKanbanView);
viewRegistry.add('account_dashboard_kanban', DashboardKanbanView);
});
// Account Move Kanban View
export class AccountMoveUploadKanbanRenderer extends KanbanRenderer {
setup() {
super.setup();
this.state.dropzoneVisible = false;
}
}
AccountMoveUploadKanbanRenderer.template = "account.KanbanRenderer";
AccountMoveUploadKanbanRenderer.components = {
...KanbanRenderer.components,
AccountDropZone,
};
export class AccountMoveUploadKanbanController extends KanbanController {}
AccountMoveUploadKanbanController.components = {
...KanbanController.components,
AccountFileUploader,
};
export const AccountMoveUploadKanbanView = {
...kanbanView,
Controller: AccountMoveUploadKanbanController,
Renderer: AccountMoveUploadKanbanRenderer,
buttonTemplate: "account.KanbanView.Buttons",
};
// Accounting Dashboard
export class DashboardKanbanRecord extends KanbanRecord {
setup() {
super.setup();
this.state = useState({
dropzoneVisible: false,
});
}
}
DashboardKanbanRecord.components = {
...DashboardKanbanRecord.components,
AccountDropZone,
AccountFileUploader,
};
DashboardKanbanRecord.template = "account.DashboardKanbanRecord";
export class DashboardKanbanRenderer extends KanbanRenderer {}
DashboardKanbanRenderer.components = {
...KanbanRenderer.components,
KanbanRecord: DashboardKanbanRecord,
};
export const DashboardKanbanView = {
...kanbanView,
Renderer: DashboardKanbanRenderer,
};
registry.category("views").add("account_tree", AccountMoveUploadListView);
registry.category("views").add("account_documents_kanban", AccountMoveUploadKanbanView);
registry.category("views").add("account_dashboard_kanban", DashboardKanbanView);
@@ -0,0 +1,160 @@
odoo.define('account.upload.document', function (require) {
"use strict";
var core = require('web.core');
var qweb = core.qweb;
var ListController = require('web.ListController');
var ListView = require('web.ListView');
var KanbanController = require('web.KanbanController');
var KanbanView = require('web.KanbanView');
var viewRegistry = require('web.view_registry');
var UploadDocumentMixin = {
events: {
'click .o_button_upload_document': '_onUpload',
'change .o_account_document_upload .o_form_binary_form': '_onAddAttachment',
'drop .o_drop_area': '_onDrop',
'dragenter .o_content': '_highlight',
'dragleave .o_drop_area': '_unhighlight',
'dragover .o_drop_area': '_clear',
},
start: async function () {
// define a unique uploadId and a callback method
this.fileUploadID = _.uniqueId('account_bill_file_upload');
$(window).on(this.fileUploadID, this._onFileUploaded.bind(this));
let result = await this._super.apply(this, arguments);
await this.$(this._dropZone).prepend($(qweb.render('account.DocumentDropZone')));
return result;
},
reload: async function () {
let result = await this._super.apply(this, arguments);
await this.$(this._dropZone).prepend($(qweb.render('account.DocumentDropZone')));
return result;
},
_onAddAttachment: function (ev) {
// Auto submit form once we've selected an attachment
var $input = $(ev.currentTarget).find('input.o_input_file');
if ($input.val() !== '') {
var $binaryForm = this.$('.o_account_document_upload form.o_form_binary_form');
$binaryForm.submit();
}
},
_onFileUploaded: function () {
// Callback once attachment have been created, create a document with attachment ids
var self = this;
var attachments = Array.prototype.slice.call(arguments, 1);
// Get id from result
var attachent_ids = attachments.reduce(function(filtered, record) {
if (record.id) {
filtered.push(record.id);
}
return filtered;
}, []);
return this._rpc({
model: 'account.journal',
method: 'create_document_from_attachment',
args: ["", attachent_ids],
context: this.initialState.context,
}).then(function(result) {
self.do_action(result);
}).catch(function () {
// Reset the file input, allowing to select again the same file if needed
self.$('.o_account_document_upload .o_input_file').val('');
});
},
_createForm: function() {
// If hidden upload form don't exists, create it
var $formContainer = this.$('.o_content').find('.o_account_document_upload');
if (!$formContainer.length) {
$formContainer = $(qweb.render('account.DocumentsHiddenUploadForm', {widget: this}));
$formContainer.appendTo(this.$('.o_content'));
}
},
_onUpload: function (ev) {
this._createForm();
// Trigger the input to select a file
this.$('.o_account_document_upload .o_input_file').click();
},
_highlight: function(ev) {
$('.o_drop_area').show();
},
_unhighlight: function(ev) {
$('.o_drop_area').hide();
},
_clear: function(ev) {
ev.preventDefault();
},
_onDrop: function (ev) {
ev.preventDefault();
this._createForm();
if (ev.originalEvent.dataTransfer.files.length) {
this.$('.o_account_document_upload .o_input_file')[0].files = ev.originalEvent.dataTransfer.files;
this.$('.o_form_binary_form')[0].submit();
}
this._unhighlight(ev);
},
}
var DocumentsListView = ListView.extend({
config: Object.assign({}, ListView.prototype.config, {
Controller: ListController.extend({_dropZone: '.o_content'}, UploadDocumentMixin, {
buttons_template: 'DocumentsListView.buttons',
events: Object.assign({}, ListController.prototype.events, UploadDocumentMixin.events),
}),
}),
});
var DocumentsKanbanView = KanbanView.extend({
config: Object.assign({}, KanbanView.prototype.config, {
Controller: KanbanController.extend({_dropZone: '.o_content'}, UploadDocumentMixin, {
buttons_template: 'DocumentsKanbanView.buttons',
events: Object.assign({}, KanbanController.prototype.events, UploadDocumentMixin.events),
}),
}),
});
var DashboardKanbanController = KanbanController.extend({_dropZone: '.o_kanban_record'}, UploadDocumentMixin, {
events: Object.assign({}, KanbanController.prototype.events, UploadDocumentMixin.events),
_setJournalContext: function(ev) {
var kanbanRecord = $(ev.currentTarget).closest('.o_kanban_record').data('record');
this.initialState.context['default_journal_id'] = kanbanRecord.id;
let journal_type = kanbanRecord.state.data.type;
if (journal_type == 'sale') {
this.initialState.context['default_move_type'] = 'out_invoice';
} else if (journal_type == 'purchase') {
this.initialState.context['default_move_type'] = 'in_invoice';
}
},
_onUpload: function (ev) {
this._setJournalContext(ev);
UploadDocumentMixin._onUpload.apply(this, arguments);
},
_onDrop: function (ev) {
this._setJournalContext(ev);
UploadDocumentMixin._onDrop.apply(this, arguments);
},
});
var DashboardKanbanView = KanbanView.extend({
config: Object.assign({}, KanbanView.prototype.config, {
Controller: DashboardKanbanController,
}),
});
viewRegistry.add('account_tree', DocumentsListView);
viewRegistry.add('account_documents_kanban', DocumentsKanbanView);
viewRegistry.add('account_dashboard_kanban', DashboardKanbanView);
});
@@ -1,5 +1,4 @@
.o_drop_area {
display: none;
width: 100%;
height: 100%;
position: absolute;
@@ -1,37 +1,88 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates>
<t t-name="account.DocumentsHiddenUploadForm">
<div class="d-none o_account_document_upload">
<t t-call="HiddenInputFile">
<t t-set="multi_upload" t-value="true"/>
<t t-set="fileupload_id" t-value="widget.fileUploadID"/>
<t t-set="fileupload_action" t-translation="off">/web/binary/upload_attachment</t>
<input type="hidden" name="model" value=""/>
<input type="hidden" name="id" value="0"/>
</t>
</div>
</t>
<t t-extend="ListView.buttons" t-name="DocumentsListView.buttons">
<t t-jquery="button.o_list_button_add" t-operation="after">
<button type="button" class="btn btn-secondary o_button_upload_document">
Upload
</button>
</t>
</t>
<t t-extend="KanbanView.buttons" t-name="DocumentsKanbanView.buttons">
<t t-jquery="button" t-operation="after">
<button type="button" class="btn btn-secondary o_button_upload_document">
Upload
</button>
</t>
</t>
<t t-name="account.DocumentDropZone">
<div class="o_drop_area">
<t t-name="account.DropZone" owl="1">
<div t-if="props.visible"
t-attf-class="o_drop_area"
t-on-dragover.prevent="()=>{}"
t-on-dragleave="props.hideZone"
t-on-drop.prevent="onDrop">
<i class="fa fa-upload fa-10x"></i>
</div>
</t>
<t t-name="account.AccountFileUploader" owl="1">
<div>
<FileUploader
acceptedFileExtensions="props.acceptedFileExtensions"
fileUploadClass="'account_file_uploader'"
multiUpload="true"
onUploaded.bind="onFileUploaded"
onUploadComplete.bind="onUploadComplete">
<t t-set-slot="toggler">
<t t-slot="toggleButton"/>
</t>
</FileUploader>
</div>
</t>
<t t-name="account.ListRenderer" t-inherit="web.ListRenderer" t-inherit-mode="primary" owl="1">
<xpath expr="//div[@t-ref='root']" position="before">
<AccountDropZone
visible="state.dropzoneVisible"
hideZone="() => state.dropzoneVisible = false"/>
</xpath>
<xpath expr="//div[@t-ref='root']" position="attributes">
<attribute name="t-on-dragenter.stop.prevent">() => state.dropzoneVisible = true</attribute>
</xpath>
</t>
<t t-name="account.KanbanRenderer" t-inherit="web.KanbanRenderer" t-inherit-mode="primary" owl="1">
<xpath expr="//div[@t-ref='root']" position="before">
<AccountDropZone
visible="state.dropzoneVisible"
hideZone="() => state.dropzoneVisible = false"/>
</xpath>
<xpath expr="//div[@t-ref='root']" position="attributes">
<attribute name="t-on-dragenter.stop.prevent">() => state.dropzoneVisible = true</attribute>
</xpath>
</t>
<t t-name="account.ListView.Buttons" t-inherit="web.ListView.Buttons" t-inherit-mode="primary" owl="1">
<xpath expr="//*[@class='btn btn-primary o_list_button_add']" position="after">
<AccountFileUploader extraContext="props.context">
<t t-set-slot="toggleButton">
<button type="button" class="btn btn-secondary o_button_upload_bill">
Upload
</button>
</t>
</AccountFileUploader>
</xpath>
</t>
<t t-name="account.KanbanView.Buttons" t-inherit="web.KanbanView.Buttons" t-inherit-mode="primary" owl="1">
<xpath expr="//*[@class='btn btn-primary o-kanban-button-new']" position="after">
<AccountFileUploader extraContext="props.context">
<t t-set-slot="toggleButton">
<button type="button" class="btn btn-secondary o_button_upload_bill">
Upload
</button>
</t>
</AccountFileUploader>
</xpath>
</t>
<t t-name="account.DashboardKanbanRecord" owl="1">
<div
role="article"
t-att-class="getRecordClasses()"
t-att-data-id="props.canResequence and props.record.id"
t-att-tabindex="props.record.model.useSampleModel ? -1 : 0"
t-on-click="onGlobalClick"
t-on-dragenter.stop.prevent="() => state.dropzoneVisible = true"
t-ref="root">
<t t-call="{{ templates['kanban-box'] }}"/>
</div>
</t>
</templates>
@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates>
<t t-name="account.DocumentsHiddenUploadForm">
<div class="d-none o_account_document_upload">
<t t-call="HiddenInputFile">
<t t-set="multi_upload" t-value="true"/>
<t t-set="fileupload_id" t-value="widget.fileUploadID"/>
<t t-set="fileupload_action" t-translation="off">/web/binary/upload_attachment</t>
<input type="hidden" name="model" value=""/>
<input type="hidden" name="id" value="0"/>
</t>
</div>
</t>
<t t-extend="ListView.buttons" t-name="DocumentsListView.buttons">
<t t-jquery="button.o_list_button_add" t-operation="after">
<button type="button" class="btn btn-secondary o_button_upload_document">
Upload
</button>
</t>
</t>
<t t-extend="KanbanView.buttons" t-name="DocumentsKanbanView.buttons">
<t t-jquery="button" t-operation="after">
<button type="button" class="btn btn-secondary o_button_upload_document">
Upload
</button>
</t>
</t>
<t t-name="account.DocumentDropZone">
<div class="o_drop_area">
<i class="fa fa-upload fa-10x"></i>
</div>
</t>
</templates>
@@ -18,6 +18,7 @@
<div t-attf-class="#{!selection_mode ? kanban_color(record.color.raw_value) : ''}">
<t t-value="JSON.parse(record.kanban_dashboard.raw_value)" t-set="dashboard"/>
<t t-value="record.type.raw_value" t-set="journal_type"/>
<t t-call="AccountDropZoneUploader"/>
<t t-call="JournalTop"/>
<div t-att-class="'container o_kanban_card_content' + (dashboard.is_sample_data ? ' o_sample_data' : '')">
<div class="row">
@@ -36,6 +37,17 @@
</div>
</t>
<t t-name="AccountDropZoneUploader" owl="1">
<AccountDropZone
visible="state.dropzoneVisible"
hideZone="() => state.dropzoneVisible = false"/>
<AccountFileUploader
extraContext="{
default_journal_id: record.id.raw_value,
default_move_type: journal_type == 'sale' and 'out_invoice' or journal_type == 'purchase' and 'in_invoice' or 'entry'
}"/>
</t>
<t t-name="JournalTop">
<div t-attf-class="o_kanban_card_header">
<div class="o_kanban_card_header_title">