Compare commits

...

36 Commits

Author SHA1 Message Date
hparfr
955cda6957 has_discount collides with account_cash_discount
rename here because it's easier
2023-04-12 12:15:00 +02:00
David Beal
d2cf9b73d8 FIX dev_menu: black 2023-03-23 13:55:37 +01:00
David Beal
d6cf5f82e7 Update menu_view.xml 2023-03-23 13:46:55 +01:00
Alexis de Lattre
e9350bac57 account_usability: reversal wizard: don't set D+1 default date when we try to generate a refund 2023-03-21 14:29:32 +01:00
Alexis de Lattre
e8b43b67c1 account_bank_reconciliation_summary_xlsx: fix logic error in reconcile filter 2023-03-09 23:30:13 +01:00
Alexis de Lattre
f2b5b0b4dd account_usability: remove acc_type from res.partner.bank tree view
I now recommand the use of partner_bank_acc_type_constraint from https://github.com/OCA/partner-contact/pull/1474
2023-03-09 23:16:16 +01:00
Alexis de Lattre
a64c60a540 product_print_zpl_barcode: fix warning and improve code 2023-03-01 23:25:45 +01:00
Alexis de Lattre
b48db5492d Add module stock_quant_package_move_wizard 2023-02-21 15:10:38 +01:00
Alexis de Lattre
9026660416 product_category_tax: add field in constraint 2023-02-13 22:24:05 +01:00
Alexis de Lattre
d2a9f953b9 account_usability: Add confirm pop-up on 'reset to new' button 2023-01-23 17:23:46 +01:00
Alexis de Lattre
acff0a421d product_print_zpl_barcode: 2 fields: png and svg 2023-01-23 09:14:44 +01:00
Alexis de Lattre
64f54a9389 product_print_zpl_barcode: Add field barcode_image for EAN13 and EAN8 on product.product 2023-01-22 22:39:08 +01:00
Alexis de Lattre
08db759977 mail_usability: improve mail_activity views 2023-01-20 19:31:11 +01:00
Alexis de Lattre
e07df6b45a mail_usability: Improve view of mail.activity 2023-01-20 12:02:43 +01:00
Alexis de Lattre
fc58a9adf5 Add module product_detailed_type and product_detailed_type_stock 2023-01-20 12:02:24 +01:00
Alexis de Lattre
1d463a744d Add module product_priority_star 2023-01-20 12:01:57 +01:00
Alexis de Lattre
dbad21c13a Add module sale_crm_usability 2023-01-19 17:15:56 +01:00
Alexis de Lattre
aa16b70bdd Add module account_bank_reconciliation_summary_xlsx 2023-01-13 11:33:48 +01:00
Alexis de Lattre
eff63f2be2 crm_usability: CRM>Sales>My activity should not be limited to Sales Manager 2023-01-03 10:36:13 +01:00
Alexis de Lattre
937595ca2c product_usability: use ondelete='restrict' instead of 'cascade' on supplierinfo/partner_id 2022-12-12 21:45:12 +01:00
Raphaël Valyi
9397bb9fce Merge pull request #189 from akretion/14.0-style-fix
[14.0][account_usability] style fix
2022-12-06 11:42:11 -03:00
Raphaël Valyi
390ce75827 [FIX] 4 chars indent + reordered imports 2022-12-05 20:04:50 -03:00
Raphaël Valyi
819d145763 Merge pull request #188 from akretion/14.0-fix-account_invoice_update_wizard_payment_mode
[14.0][FIX] account_invoice_update_wizard_payment_mode view dependency
2022-11-29 17:15:26 -03:00
clementmbr
45bbcc0cb3 [FIX] account_invoice_update_wizard_payment_mode view dependency 2022-11-29 16:58:25 -03:00
Hpar
6ddc1b86d5 Merge pull request #187 from akretion/eradicate-quick-create-fix-hook
eradicate_quickreate: fix hook
2022-11-07 14:54:29 +01:00
Alexis de Lattre
d0b315f648 [MIG] sale_quotation_title to v14 2022-11-07 12:41:08 +01:00
Hpar
f1eeaa2e8a eradicate_quickreate: fix hook
if the record already exists and is False, we shouldn't create a new record
2022-10-31 15:44:37 +01:00
beau sebastien
553f05c58f Merge pull request #186 from akretion/14.0-add-sale-show-transaction
add sale_show_transaction, that make transaction really visible on sale order
2022-10-28 09:20:27 +02:00
Alexis de Lattre
50ae5dc9cb purchase_usability: remove code now that upstream fix is merged 2022-10-27 23:17:34 +02:00
beau sebastien
9c7775dabb Merge pull request #179 from akretion/14.0-imp-account_invoice_update_wizard
account_invoice_update_wizard: improve UI
2022-10-27 23:13:17 +02:00
Sébastien BEAU
70f1f13edd account_invoice_update_wizard: inactive update of broken payment term
We should add test and fix it
2022-10-27 23:12:02 +02:00
Sébastien BEAU
989960c7c8 account_invoice_update_wizard: fix UI add translation
Add section support and keep order based on the line sequence
Add Fr translation
2022-10-27 23:12:02 +02:00
Sébastien BEAU
9b186028c3 account_usability: aml search on move_id first, then on name/ref 2022-10-27 23:00:22 +02:00
Sébastien BEAU
c2c4957686 account_usability: improve partial reconcile matching_number 2022-10-27 23:00:22 +02:00
Sébastien BEAU
d382aea22f sale_show_transaction: better naming 2022-10-21 22:48:28 +02:00
Sébastien BEAU
688a07fc5e add sale_show_transaction, that make transaction really visible on sale order 2022-10-21 16:30:39 +02:00
83 changed files with 2649 additions and 114 deletions

View File

@@ -0,0 +1,27 @@
===============================
Bank Reconciliation Report XLSX
===============================
In Odoo v13+, a bank reconciliation report is not really needed because all the payments executed that are not debited/credited on the bank account are in separate waiting accounts. But accountants want a bank reconciliation report, so this module adds one, even if it is quite different from a classic bank reconciliation report.
Configuration
=============
This module doesn't require any configuration.
Usage
=====
You can launch the Bank Reconciliation Report wizard from:
* the menu *Accounting > Reports > Bank > Bank Reconciliation*,
* the invoicing dashboard: on a bank journal, click on the options, then select *Bank Reconciliation*.
* the form view of a bank statement: click on the button *Bank Reconciliation Report*.
Credits
=======
Contributors
------------
* Alexis de Lattre <alexis.delattre@akretion.com>

View File

@@ -0,0 +1,2 @@
from . import report
from . import wizard

View File

@@ -0,0 +1,21 @@
# Copyright 2017-2023 Akretion France (http://www.akretion.com/)
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{
"name": "Bank Reconciliation Report",
"version": "14.0.1.0.0",
"license": "AGPL-3",
"author": "Akretion",
"website": "https://github.com/akretion/odoo-usability",
"summary": "Bank reconciliation XLSX report",
"depends": ["account", "report_xlsx"],
"data": [
"report/report.xml",
"wizard/bank_reconciliation_report_wizard_view.xml",
"views/account_bank_statement.xml",
"views/account_journal.xml",
"security/ir.model.access.csv",
],
"installable": True,
}

View File

@@ -0,0 +1,213 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * account_bank_reconciliation_summary_xlsx
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 14.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-01-13 10:31+0000\n"
"PO-Revision-Date: 2023-01-13 10:31+0000\n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: account_bank_reconciliation_summary_xlsx
#: code:addons/account_bank_reconciliation_summary_xlsx/report/bank_reconciliation_xlsx.py:0
#, python-format
msgid "Amount"
msgstr "Montant"
#. module: account_bank_reconciliation_summary_xlsx
#: code:addons/account_bank_reconciliation_summary_xlsx/report/bank_reconciliation_xlsx.py:0
#, python-format
msgid "Balance %s:"
msgstr "Solde %s :"
#. module: account_bank_reconciliation_summary_xlsx
#: model:ir.model.fields,field_description:account_bank_reconciliation_summary_xlsx.field_bank_reconciliation_report_wizard__journal_ids
msgid "Bank Journals"
msgstr "Journaux de banque"
#. module: account_bank_reconciliation_summary_xlsx
#: model:ir.actions.act_window,name:account_bank_reconciliation_summary_xlsx.bank_reconciliation_report_wizard_action
#: model:ir.ui.menu,name:account_bank_reconciliation_summary_xlsx.bank_reconciliation_report_wizard_menu
#: model_terms:ir.ui.view,arch_db:account_bank_reconciliation_summary_xlsx.account_journal_dashboard_kanban_view
msgid "Bank Reconciliation"
msgstr "Rapprochement bancaire"
#. module: account_bank_reconciliation_summary_xlsx
#: code:addons/account_bank_reconciliation_summary_xlsx/report/bank_reconciliation_xlsx.py:0
#: model_terms:ir.ui.view,arch_db:account_bank_reconciliation_summary_xlsx.view_bank_statement_form
#, python-format
msgid "Bank Reconciliation Report"
msgstr "Rapport de rapprochement bancaire"
#. module: account_bank_reconciliation_summary_xlsx
#: model:ir.model,name:account_bank_reconciliation_summary_xlsx.model_bank_reconciliation_report_wizard
msgid "Bank Reconciliation Report Wizard"
msgstr "Assistant rapport de rapprochement bancaire"
#. module: account_bank_reconciliation_summary_xlsx
#: model:ir.actions.report,name:account_bank_reconciliation_summary_xlsx.bank_reconciliation_xlsx
msgid "Bank Reconciliation XLSX"
msgstr "Rapprochement bancaire XLSX"
#. module: account_bank_reconciliation_summary_xlsx
#: model:ir.model,name:account_bank_reconciliation_summary_xlsx.model_report_bank_reconciliation_xlsx
msgid "Bank Reconciliation XLSX Report"
msgstr "Rapport de rapprochement bancaire XLSX"
#. module: account_bank_reconciliation_summary_xlsx
#: model:ir.ui.menu,name:account_bank_reconciliation_summary_xlsx.menu_report_bank_root
msgid "Bank Reports"
msgstr "Rapports bancaires"
#. module: account_bank_reconciliation_summary_xlsx
#: model_terms:ir.ui.view,arch_db:account_bank_reconciliation_summary_xlsx.bank_reconciliation_report_wizard_form
msgid "Cancel"
msgstr "Annuler"
#. module: account_bank_reconciliation_summary_xlsx
#: code:addons/account_bank_reconciliation_summary_xlsx/report/bank_reconciliation_xlsx.py:0
#: model:ir.model.fields,field_description:account_bank_reconciliation_summary_xlsx.field_bank_reconciliation_report_wizard__company_id
#, python-format
msgid "Company"
msgstr "Société"
#. module: account_bank_reconciliation_summary_xlsx
#: code:addons/account_bank_reconciliation_summary_xlsx/report/bank_reconciliation_xlsx.py:0
#, python-format
msgid "Counter-part"
msgstr "Contre partie"
#. module: account_bank_reconciliation_summary_xlsx
#: model:ir.model.fields,field_description:account_bank_reconciliation_summary_xlsx.field_bank_reconciliation_report_wizard__create_uid
msgid "Created by"
msgstr "Créé par"
#. module: account_bank_reconciliation_summary_xlsx
#: model:ir.model.fields,field_description:account_bank_reconciliation_summary_xlsx.field_bank_reconciliation_report_wizard__create_date
msgid "Created on"
msgstr "Créé le"
#. module: account_bank_reconciliation_summary_xlsx
#: code:addons/account_bank_reconciliation_summary_xlsx/report/bank_reconciliation_xlsx.py:0
#: code:addons/account_bank_reconciliation_summary_xlsx/report/bank_reconciliation_xlsx.py:0
#: model:ir.model.fields,field_description:account_bank_reconciliation_summary_xlsx.field_bank_reconciliation_report_wizard__date
#, python-format
msgid "Date"
msgstr ""
#. module: account_bank_reconciliation_summary_xlsx
#: model:ir.model.fields,field_description:account_bank_reconciliation_summary_xlsx.field_bank_reconciliation_report_wizard__display_name
#: model:ir.model.fields,field_description:account_bank_reconciliation_summary_xlsx.field_report_bank_reconciliation_xlsx__display_name
msgid "Display Name"
msgstr ""
#. module: account_bank_reconciliation_summary_xlsx
#: model:ir.model.fields.selection,name:account_bank_reconciliation_summary_xlsx.selection__bank_reconciliation_report_wizard__move_state__draft_posted
msgid "Draft and Posted Entries"
msgstr "Écritures brouillon et comptabilisées"
#. module: account_bank_reconciliation_summary_xlsx
#: code:addons/account_bank_reconciliation_summary_xlsx/report/bank_reconciliation_xlsx.py:0
#: model:ir.model.fields,field_description:account_bank_reconciliation_summary_xlsx.field_bank_reconciliation_report_wizard__move_state
#, python-format
msgid "Entries"
msgstr "Écritures"
#. module: account_bank_reconciliation_summary_xlsx
#: model_terms:ir.ui.view,arch_db:account_bank_reconciliation_summary_xlsx.bank_reconciliation_report_wizard_form
msgid "Export XLSX"
msgstr ""
#. module: account_bank_reconciliation_summary_xlsx
#: code:addons/account_bank_reconciliation_summary_xlsx/report/bank_reconciliation_xlsx.py:0
#, python-format
msgid "Generated on %s"
msgstr "Généré le %s"
#. module: account_bank_reconciliation_summary_xlsx
#: model:ir.model.fields,field_description:account_bank_reconciliation_summary_xlsx.field_bank_reconciliation_report_wizard__id
#: model:ir.model.fields,field_description:account_bank_reconciliation_summary_xlsx.field_report_bank_reconciliation_xlsx__id
msgid "ID"
msgstr ""
#. module: account_bank_reconciliation_summary_xlsx
#: code:addons/account_bank_reconciliation_summary_xlsx/report/bank_reconciliation_xlsx.py:0
#, python-format
msgid "Journal"
msgstr ""
#. module: account_bank_reconciliation_summary_xlsx
#: code:addons/account_bank_reconciliation_summary_xlsx/report/bank_reconciliation_xlsx.py:0
#, python-format
msgid "Label"
msgstr "Libellé"
#. module: account_bank_reconciliation_summary_xlsx
#: model:ir.model.fields,field_description:account_bank_reconciliation_summary_xlsx.field_bank_reconciliation_report_wizard____last_update
#: model:ir.model.fields,field_description:account_bank_reconciliation_summary_xlsx.field_report_bank_reconciliation_xlsx____last_update
msgid "Last Modified on"
msgstr ""
#. module: account_bank_reconciliation_summary_xlsx
#: model:ir.model.fields,field_description:account_bank_reconciliation_summary_xlsx.field_bank_reconciliation_report_wizard__write_uid
msgid "Last Updated by"
msgstr ""
#. module: account_bank_reconciliation_summary_xlsx
#: model:ir.model.fields,field_description:account_bank_reconciliation_summary_xlsx.field_bank_reconciliation_report_wizard__write_date
msgid "Last Updated on"
msgstr ""
#. module: account_bank_reconciliation_summary_xlsx
#: code:addons/account_bank_reconciliation_summary_xlsx/report/bank_reconciliation_xlsx.py:0
#, python-format
msgid "Move Number"
msgstr "Numéro de pièce"
#. module: account_bank_reconciliation_summary_xlsx
#: code:addons/account_bank_reconciliation_summary_xlsx/report/bank_reconciliation_xlsx.py:0
#, python-format
msgid "No bank journal selected."
msgstr "Aucun journal de banque sélectionné."
#. module: account_bank_reconciliation_summary_xlsx
#: code:addons/account_bank_reconciliation_summary_xlsx/report/bank_reconciliation_xlsx.py:0
#, python-format
msgid "None"
msgstr "Aucun"
#. module: account_bank_reconciliation_summary_xlsx
#: code:addons/account_bank_reconciliation_summary_xlsx/report/bank_reconciliation_xlsx.py:0
#, python-format
msgid "Partner"
msgstr "Partenaire"
#. module: account_bank_reconciliation_summary_xlsx
#: model:ir.model.fields.selection,name:account_bank_reconciliation_summary_xlsx.selection__bank_reconciliation_report_wizard__move_state__posted
msgid "Posted Entries"
msgstr "Écritures comptabilisées"
#. module: account_bank_reconciliation_summary_xlsx
#: code:addons/account_bank_reconciliation_summary_xlsx/report/bank_reconciliation_xlsx.py:0
#, python-format
msgid "Ref."
msgstr "Réf."
#. module: account_bank_reconciliation_summary_xlsx
#: code:addons/account_bank_reconciliation_summary_xlsx/report/bank_reconciliation_xlsx.py:0
#, python-format
msgid "Sub-total:"
msgstr "Sous-total :"
#. module: account_bank_reconciliation_summary_xlsx
#: code:addons/account_bank_reconciliation_summary_xlsx/report/bank_reconciliation_xlsx.py:0
#, python-format
msgid "TOTAL:"
msgstr "TOTAL :"

View File

@@ -0,0 +1 @@
from . import bank_reconciliation_xlsx

View File

@@ -0,0 +1,296 @@
# Copyright 2017-2023 Akretion France (http://www.akretion.com/)
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import _, models
from odoo.exceptions import UserError
from datetime import datetime
from odoo.tools.misc import format_datetime
import pytz
class BankReconciliationXlsx(models.AbstractModel):
_name = "report.bank.reconciliation.xlsx"
_description = "Bank Reconciliation XLSX Report"
_inherit = "report.report_xlsx.abstract"
def _domain_add_move_state(self, wizard, domain):
if wizard.move_state == 'posted':
domain.append(('parent_state', '=', 'posted'))
elif wizard.move_state == 'draft_posted':
domain.append(('parent_state', 'in', ('draft', 'posted')))
def _get_account_balance(self, account, wizard):
domain = [
('account_id', '=', account.id),
('date', '<=', wizard.date),
('company_id', '=', wizard.company_id.id),
]
self._domain_add_move_state(wizard, domain)
res_rg = self.env['account.move.line'].read_group(domain, ['balance:sum'], [])
account_bal = res_rg and res_rg[0].get('balance', 0.0) or 0.0
return account_bal
def _prepare_payment_move_lines(self, journal, account, wizard, unreconciled_only=True):
domain = [
("company_id", "=", wizard.company_id.id),
("account_id", "=", account.id),
("journal_id", "=", journal.id),
("date", "<=", wizard.date),
]
if unreconciled_only:
limit_datetime_naive = datetime.combine(wizard.date, datetime.max.time())
tz = pytz.timezone(self.env.user.tz)
limit_datetime_aware = tz.localize(limit_datetime_naive)
limit_datetime_utc = limit_datetime_aware.astimezone(pytz.utc)
limit_datetime = limit_datetime_utc.replace(tzinfo=None)
domain += [
'|', ('full_reconcile_id', '=', False),
('full_reconcile_id.create_date', '>', limit_datetime)]
self._domain_add_move_state(wizard, domain)
mlines = self.env["account.move.line"].search(domain)
res = []
for mline in mlines:
move = mline.move_id
cpart = []
for line in move.line_ids:
if (
line.account_id != account
and line.account_id.code not in cpart
):
cpart.append(line.account_id.code)
counterpart = " ,".join(cpart)
res.append(
{
"date": mline.date,
"ref": move.ref or "",
"label": mline.name,
"partner": mline.partner_id.display_name or "",
"amount": mline.balance,
"move_name": move.name,
"counterpart": counterpart,
}
)
return res
def _write_move_lines_block(self, jdi, row, account, add2total=True):
sheet = jdi['sheet']
style = jdi['style']
style_suffix = not add2total and '_warn' or ''
subtotal = 0.0
mlines = self._prepare_payment_move_lines(jdi['journal'], account, jdi['wizard'])
if mlines or add2total:
sheet.write(row, 0, '%s %s' % (account.name, account.code), style['title' + style_suffix])
sheet.write(row, 1, "", style['title' + style_suffix])
if not mlines:
if add2total:
sheet.write(row, 2, _("None"), style['none'])
else:
return
else:
row += 1
col_labels = [
_("Date"),
_("Partner"),
_("Amount"),
_("Move Number"),
_("Counter-part"),
_("Ref."),
_("Label"),
]
col = 0
for col_label in col_labels:
sheet.write(row, col, col_label, style['col_header'])
col += 1
row += 1
start_line = row + 1
for mline in mlines:
sheet.write(row, 0, mline["date"], style['regular_date'])
sheet.write(row, 1, mline["partner"], style['regular'])
sheet.write(row, 2, mline["amount"], style['currency'])
sheet.write(row, 3, mline["move_name"], style['regular'])
sheet.write(row, 4, mline["counterpart"], style['regular'])
sheet.write(row, 5, mline["ref"], style['regular'])
sheet.write(row, 6, mline["label"], style['regular'])
subtotal += mline["amount"]
row += 1
end_line = row
for col in range(1):
sheet.write(row, col, "", style['title' + style_suffix])
sheet.write(row, 1, _("Sub-total:") + ' ', style['title_right' + style_suffix])
formula = '=SUM(%s%d:%s%d)' % (
jdi['total_col'], start_line, jdi['total_col'], end_line)
sheet.write_formula(row, 2, formula, style['currency_bg' + style_suffix], subtotal)
if add2total:
jdi['total'] += subtotal
jdi['total_formula'] += '+%s%d' % (jdi['total_col'], row + 1)
return row
def generate_xlsx_report(self, workbook, data, wizard):
if not wizard.journal_ids:
raise UserError(_("No bank journal selected."))
date_dt = wizard.date
company = wizard.company_id
style = self._get_style(workbook, company)
move_state_label = dict(
wizard.fields_get('move_state', 'selection')['move_state']['selection'])
generated_on_label = _('Generated on %s') % format_datetime(
self.env, datetime.utcnow())
for journal in wizard.journal_ids:
row = 0
sheet = workbook.add_worksheet(journal.code or journal.name)
jdi = {
'wizard': wizard,
'journal': journal,
'style': style,
'sheet': sheet,
'total': 0.0,
'total_formula': '=',
'total_col': 'C',
}
sheet.write(
row,
0,
_("Bank Reconciliation Report"),
style['doc_title'],
)
row += 1
sheet.write(row, 0, generated_on_label, style['small'])
sheet.set_row(0, 26)
sheet.set_column(0, 0, 10)
sheet.set_column(1, 1, 35)
sheet.set_column(2, 2, 15)
sheet.set_column(3, 3, 15)
sheet.set_column(4, 4, 25)
sheet.set_column(5, 5, 30)
sheet.set_column(6, 6, 60)
row += 3
sheet.write(row, 0, _("Company"), style['wizard_field'])
sheet.write(row, 1, wizard.company_id.display_name, style['wizard_value'])
row += 1
sheet.write(row, 0, _("Date"), style['wizard_field'])
sheet.write(row, 1, date_dt, style['wizard_value_date'])
row += 1
sheet.write(row, 0, _("Journal"), style['wizard_field'])
sheet.write(row, 1, journal.display_name, style['wizard_value'])
row += 1
sheet.write(row, 0, _("Entries"), style['wizard_field'])
sheet.write(row, 1, move_state_label[wizard.move_state], style['wizard_value'])
# 1) Show balance of bank account
row += 3
bank_account = journal.default_account_id
for col in range(1):
sheet.write(row, col, "", style['title'])
sheet.write(row, 1, _("Balance %s:") % bank_account.code + ' ', style['title_right'])
account_bal = self._get_account_balance(bank_account, wizard)
sheet.write(row, 2, account_bal, style['currency_bg'])
jdi['total'] += account_bal
jdi['total_formula'] += '%s%d' % (jdi['total_col'], row + 1)
row += 2
# 2) Show payment lines IN (debit)
debit_account = journal.payment_debit_account_id
row = self._write_move_lines_block(jdi, row, debit_account)
row += 2
# 3) Show payment lines OUT (credit)
credit_account = journal.payment_credit_account_id
row = self._write_move_lines_block(jdi, row, credit_account)
row += 2
for col in range(1):
sheet.write(row, col, "", style['title'])
sheet.write(row, 1, _("TOTAL:") + ' ', style['title_right'])
sheet.write_formula(
row, 2, jdi['total_formula'], style['currency_bg'], jdi['total'])
row += 3
# 4) Show suspense account lines
row = self._write_move_lines_block(
jdi, row, journal.suspense_account_id, add2total=False)
def _get_style(self, workbook, company):
style = {}
font_size = 10
light_grey = "#eeeeee"
title_blue = "#e6e6fa"
subtotal_orange = "#ffcc00"
title_warn = "#ff9999"
subtotal_warn = "#ffff99"
light_purple = "#ffdeff"
lang_code = self.env.user.lang
lang = False
if lang_code:
lang = self.env["res.lang"].search([("code", "=", lang_code)])
if not lang:
lang = self.env["res.lang"].search([], limit=1)
xls_date_format = (
lang.date_format.replace("%Y", "yyyy")
.replace("%m", "mm")
.replace("%d", "dd")
.replace("%y", "yy")
)
style['doc_title'] = workbook.add_format(
{"bold": True, "font_size": font_size + 4})
style['small'] = workbook.add_format({"font_size": font_size - 3})
style['col_header'] = workbook.add_format(
{
"bold": True,
"bg_color": light_grey,
"text_wrap": True,
"font_size": font_size,
"align": "center",
}
)
title_style = {
"bold": True,
"bg_color": title_blue,
"font_size": font_size,
"align": "left",
}
style['title_right'] = workbook.add_format(dict(title_style, align="right"))
style['title'] = workbook.add_format(dict(title_style))
style['wizard_field'] = workbook.add_format(dict(title_style, bg_color=light_grey))
wizard_value_style = {
"bg_color": light_purple,
"bold": True,
"font_size": font_size,
"align": "left",
}
style['wizard_value'] = workbook.add_format(wizard_value_style)
style['wizard_value_date'] = workbook.add_format(
dict(wizard_value_style, num_format=xls_date_format))
style['none'] = workbook.add_format(
{"bold": True, "font_size": font_size, "align": "right", "bg_color": subtotal_orange}
)
# WARN for suspense account
style['title_warn'] = workbook.add_format(
dict(title_style, align="left", bg_color=title_warn))
style['title_right_warn'] = workbook.add_format(
dict(title_style, align="right", bg_color=title_warn))
style['regular'] = workbook.add_format({"font_size": font_size})
if "%" in xls_date_format:
# fallback
xls_date_format = "yyyy-mm-dd"
style['regular_date'] = workbook.add_format(
{"num_format": xls_date_format, "font_size": font_size, "align": "left"}
)
cur_format = "#,##0.00 %s" % (
company.currency_id.symbol or company.currency_id.name
)
# It seems that Excel replaces automatically the decimal
# and thousand separator by those of the language under which
# Excel runs
currency_style = {"num_format": cur_format, "font_size": font_size}
style['currency'] = workbook.add_format(currency_style)
style['currency_bg'] = workbook.add_format(
dict(currency_style, bg_color=subtotal_orange))
style['currency_bg_warn'] = workbook.add_format(
dict(currency_style, bg_color=subtotal_warn))
return style

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8" ?>
<!--
Copyright 2017-2023 Akretion France (http://www.akretion.com/)
@author: Alexis de Lattre <alexis.delattre@akretion.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->
<odoo>
<record id="bank_reconciliation_xlsx" model="ir.actions.report">
<field name="name">Bank Reconciliation XLSX</field>
<field name="model">bank.reconciliation.report.wizard</field>
<field name="report_type">xlsx</field>
<field name="report_name">bank.reconciliation.xlsx</field>
<field name="report_file">bank.reconciliation.xlsx</field>
<!-- print_report_name doesn't work here... -->
<field name="print_report_name">'bank_reconciliation-%s' % (object.date)</field>
</record>
</odoo>

View File

@@ -0,0 +1,3 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_bank_reconciliation_report_wizard_user,Full access on bank.reconciliation.report.wizard,model_bank_reconciliation_report_wizard,account.group_account_user,1,1,1,1
access_bank_reconciliation_report_wizard_readonly,Full access on bank.reconciliation.report.wizard,model_bank_reconciliation_report_wizard,account.group_account_readonly,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_bank_reconciliation_report_wizard_user Full access on bank.reconciliation.report.wizard model_bank_reconciliation_report_wizard account.group_account_user 1 1 1 1
3 access_bank_reconciliation_report_wizard_readonly Full access on bank.reconciliation.report.wizard model_bank_reconciliation_report_wizard account.group_account_readonly 1 1 1 1

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8" ?>
<!--
Copyright 2017-2020 Akretion France (http://www.akretion.com/)
@author: Alexis de Lattre <alexis.delattre@akretion.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->
<odoo>
<record id="view_bank_statement_form" model="ir.ui.view">
<field name="name">bank_rec_summary.account.bank.statement.form</field>
<field name="model">account.bank.statement</field>
<field name="inherit_id" ref="account.view_bank_statement_form" />
<field name="arch" type="xml">
<button name="button_reprocess" position="after">
<button
name="%(bank_reconciliation_report_wizard_action)d"
type="action"
string="Bank Reconciliation Report"
context="{'default_journal_ids': [journal_id]}"
/>
</button>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8" ?>
<!--
Copyright 2018-2023 Akretion France (http://www.akretion.com/)
@author: Alexis de Lattre <alexis.delattre@akretion.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->
<odoo>
<!-- Accounting Dashboard -->
<record id="account_journal_dashboard_kanban_view" model="ir.ui.view">
<field
name="name"
>bank_reconciliation_summarry.account_journal_dashboard</field>
<field name="model">account.journal</field>
<field name="inherit_id" ref="account.account_journal_dashboard_kanban_view" />
<field name="arch" type="xml">
<xpath expr="//a[@name='open_collect_money']/.." position="before">
<div name="bank_reconciliation_report">
<a
role="menuitem"
type="action"
name="%(bank_reconciliation_report_wizard_action)d"
context="{'default_journal_ids': [active_id]}"
>Bank Reconciliation</a>
</div>
</xpath>
</field>
</record>
</odoo>

View File

@@ -0,0 +1 @@
from . import bank_reconciliation_report_wizard

View File

@@ -0,0 +1,42 @@
# Copyright 2017-2023 Akretion France (http://www.akretion.com/)
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import api, fields, models
class BankReconciliationReportWizard(models.TransientModel):
_name = "bank.reconciliation.report.wizard"
_description = "Bank Reconciliation Report Wizard"
_check_company_auto = True
company_id = fields.Many2one(
'res.company', string='Company',
ondelete='cascade', required=True,
default=lambda self: self.env.company)
date = fields.Date(required=True, default=fields.Date.context_today)
move_state = fields.Selection(
[("posted", "Posted Entries"), ("draft_posted", "Draft and Posted Entries")],
string="Entries",
required=True,
default="posted",
)
journal_ids = fields.Many2many(
"account.journal",
string="Bank Journals",
domain="[('type', '=', 'bank'), ('company_id', '=', company_id)]",
required=True,
check_company=True,
default=lambda self: self._default_journal_ids(),
)
@api.model
def _default_journal_ids(self):
journals = self.env["account.journal"].search(
[
("type", "=", "bank"),
("bank_account_id", "!=", False),
("company_id", "=", self.env.company.id),
]
)
return journals

View File

@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="utf-8" ?>
<!--
Copyright 2017-2023 Akretion France (http://www.akretion.com/)
@author: Alexis de Lattre <alexis.delattre@akretion.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->
<odoo>
<record id="bank_reconciliation_report_wizard_form" model="ir.ui.view">
<field name="name">bank.reconciliation.report.wizard.form</field>
<field name="model">bank.reconciliation.report.wizard</field>
<field name="arch" type="xml">
<form>
<group name="main">
<field name="company_id" invisible="1" />
<field name="date" />
<field name="journal_ids" widget="many2many_tags" options="{'no_open': True, 'no_create': True}"/>
<field name="move_state" widget="radio"/>
</group>
<footer>
<button
name="%(account_bank_reconciliation_summary_xlsx.bank_reconciliation_xlsx)d"
string="Export XLSX"
type="action"
class="btn-primary"
/>
<button special="cancel" string="Cancel" />
</footer>
</form>
</field>
</record>
<record id="bank_reconciliation_report_wizard_action" model="ir.actions.act_window">
<field name="name">Bank Reconciliation</field>
<field name="res_model">bank.reconciliation.report.wizard</field>
<field name="view_mode">form</field>
<field name="target">new</field>
</record>
<menuitem
id="menu_report_bank_root"
parent="account.menu_finance_reports"
name="Bank Reports"
sequence="12"
/>
<menuitem
id="bank_reconciliation_report_wizard_menu"
action="bank_reconciliation_report_wizard_action"
parent="menu_report_bank_root"
sequence="10"
/>
</odoo>

View File

@@ -0,0 +1,243 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * account_invoice_update_wizard
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 14.0\n"
"Report-Msgid-Bugs-To: \n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_line_update__price_subtotal
msgid "Amount"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_line_update__analytic_account_id
msgid "Analytic Account"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_line_update__analytic_tag_ids
msgid "Analytic Tags"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_update__partner_bank_id
msgid "Bank Account"
msgstr ""
#. module: account_invoice_update_wizard
#: model_terms:ir.ui.view,arch_db:account_invoice_update_wizard.account_invoice_update_form
msgid "Bill Reference"
msgstr ""
#. module: account_invoice_update_wizard
#: model_terms:ir.ui.view,arch_db:account_invoice_update_wizard.account_invoice_update_form
msgid "Cancel"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_update__company_id
msgid "Company"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_line_update__create_uid
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_update__create_uid
msgid "Created by"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_line_update__create_date
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_update__create_date
msgid "Created on"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_line_update__currency_id
msgid "Currency"
msgstr ""
#. module: account_invoice_update_wizard
#: model_terms:ir.ui.view,arch_db:account_invoice_update_wizard.account_invoice_update_form
msgid "Customer Reference"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_line_update__name
msgid "Description"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move__display_name
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_line_update__display_name
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_update__display_name
msgid "Display Name"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_line_update__display_type
msgid "Display Type"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move__id
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_line_update__id
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_update__id
msgid "ID"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_update__invoice_id
msgid "Invoice"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_line_update__invoice_line_id
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_update__line_ids
msgid "Invoice Lines"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.actions.act_window,name:account_invoice_update_wizard.account_invoice_update_action
msgid "Invoice Update Wizard"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model,name:account_invoice_update_wizard.model_account_move
msgid "Journal Entry"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move____last_update
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_line_update____last_update
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_update____last_update
msgid "Last Modified on"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_line_update__write_uid
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_update__write_uid
msgid "Last Updated by"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_line_update__write_date
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_update__write_date
msgid "Last Updated on"
msgstr ""
#. module: account_invoice_update_wizard
#: code:addons/account_invoice_update_wizard/wizard/account_move_update.py:0
#, python-format
msgid "Non-legal fields of invoice updated via the Invoice Update wizard."
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model.fields.selection,name:account_invoice_update_wizard.selection__account_move_line_update__display_type__line_note
msgid "Note"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_update__partner_id
msgid "Partner"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_update__invoice_payment_term_id
msgid "Payment Term"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_line_update__quantity
msgid "Quantity"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_update__ref
msgid "Reference"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_update__user_id
msgid "Salesperson"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model.fields.selection,name:account_invoice_update_wizard.selection__account_move_line_update__display_type__line_section
msgid "Section"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_line_update__sequence
msgid "Sequence"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_update__invoice_origin
msgid "Source Document"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model.fields,help:account_invoice_update_wizard.field_account_move_line_update__display_type
msgid "Technical field for UX purpose."
msgstr ""
#. module: account_invoice_update_wizard
#: code:addons/account_invoice_update_wizard/wizard/account_move_update.py:0
#, python-format
msgid ""
"The original payment term '%s' doesn't have the same terms (number of terms "
"and/or amount) as the new payment term '%s'. You can only switch to a "
"payment term that has the same number of terms with the same amount."
msgstr ""
#. module: account_invoice_update_wizard
#: code:addons/account_invoice_update_wizard/wizard/account_move_update.py:0
#, python-format
msgid ""
"This wizard doesn't support the update of payment terms on an invoice which "
"is partially or fully paid."
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_update__move_type
msgid "Type"
msgstr ""
#. module: account_invoice_update_wizard
#: model_terms:ir.ui.view,arch_db:account_invoice_update_wizard.account_invoice_update_form
msgid "Update"
msgstr ""
#. module: account_invoice_update_wizard
#: model_terms:ir.ui.view,arch_db:account_invoice_update_wizard.view_move_form_inherit
msgid "Update Invoice"
msgstr ""
#. module: account_invoice_update_wizard
#: model_terms:ir.ui.view,arch_db:account_invoice_update_wizard.account_invoice_update_form
msgid "Update Invoice Wizard"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model,name:account_invoice_update_wizard.model_account_move_line_update
msgid "Update non-legal fields of invoice lines"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_line_update__parent_id
msgid "Wizard"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model,name:account_invoice_update_wizard.model_account_move_update
msgid "Wizard to update non-legal fields of invoice"
msgstr ""

View File

@@ -0,0 +1,250 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * account_invoice_update_wizard
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 14.0\n"
"Report-Msgid-Bugs-To: \n"
"Last-Translator: \n"
"Language-Team: \n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_line_update__price_subtotal
msgid "Amount"
msgstr "Montant"
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_line_update__analytic_account_id
msgid "Analytic Account"
msgstr "Compte Analytique"
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_line_update__analytic_tag_ids
msgid "Analytic Tags"
msgstr "Tag Analytique"
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_update__partner_bank_id
msgid "Bank Account"
msgstr "Compte Bancaire"
#. module: account_invoice_update_wizard
#: model_terms:ir.ui.view,arch_db:account_invoice_update_wizard.account_invoice_update_form
#, fuzzy
msgid "Bill Reference"
msgstr "Reference Client"
#. module: account_invoice_update_wizard
#: model_terms:ir.ui.view,arch_db:account_invoice_update_wizard.account_invoice_update_form
msgid "Cancel"
msgstr "Annuler"
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_update__company_id
msgid "Company"
msgstr "Société"
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_line_update__create_uid
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_update__create_uid
msgid "Created by"
msgstr "Créé par"
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_line_update__create_date
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_update__create_date
msgid "Created on"
msgstr "Créé le"
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_line_update__currency_id
msgid "Currency"
msgstr "Devise"
#. module: account_invoice_update_wizard
#: model_terms:ir.ui.view,arch_db:account_invoice_update_wizard.account_invoice_update_form
#, fuzzy
msgid "Customer Reference"
msgstr "Reference Client"
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_line_update__name
msgid "Description"
msgstr "Description"
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move__display_name
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_line_update__display_name
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_update__display_name
msgid "Display Name"
msgstr "Nom"
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_line_update__display_type
msgid "Display Type"
msgstr "Type Affichage"
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move__id
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_line_update__id
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_update__id
msgid "ID"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_update__invoice_id
msgid "Invoice"
msgstr "Facture"
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_line_update__invoice_line_id
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_update__line_ids
msgid "Invoice Lines"
msgstr "Ligne de factures"
#. module: account_invoice_update_wizard
#: model:ir.actions.act_window,name:account_invoice_update_wizard.account_invoice_update_action
msgid "Invoice Update Wizard"
msgstr "Assistance de mise à jour de la facture"
#. module: account_invoice_update_wizard
#: model:ir.model,name:account_invoice_update_wizard.model_account_move
msgid "Journal Entry"
msgstr "Entrée comptable"
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move____last_update
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_line_update____last_update
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_update____last_update
msgid "Last Modified on"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_line_update__write_uid
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_update__write_uid
msgid "Last Updated by"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_line_update__write_date
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_update__write_date
msgid "Last Updated on"
msgstr ""
#. module: account_invoice_update_wizard
#: code:addons/account_invoice_update_wizard/wizard/account_move_update.py:0
#, python-format
msgid "Non-legal fields of invoice updated via the Invoice Update wizard."
msgstr "Champs non légaux mis à jour via l'assistant"
#. module: account_invoice_update_wizard
#: model:ir.model.fields.selection,name:account_invoice_update_wizard.selection__account_move_line_update__display_type__line_note
msgid "Note"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_update__partner_id
msgid "Partner"
msgstr "Client"
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_update__invoice_payment_term_id
msgid "Payment Term"
msgstr "Condition de paiement"
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_line_update__quantity
msgid "Quantity"
msgstr "Quantité"
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_update__ref
#, fuzzy
msgid "Reference"
msgstr "Reference Client"
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_update__user_id
msgid "Salesperson"
msgstr "Vendeur"
#. module: account_invoice_update_wizard
#: model:ir.model.fields.selection,name:account_invoice_update_wizard.selection__account_move_line_update__display_type__line_section
msgid "Section"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_line_update__sequence
msgid "Sequence"
msgstr "Sequence"
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_update__invoice_origin
msgid "Source Document"
msgstr "Origine du document"
#. module: account_invoice_update_wizard
#: model:ir.model.fields,help:account_invoice_update_wizard.field_account_move_line_update__display_type
msgid "Technical field for UX purpose."
msgstr ""
#. module: account_invoice_update_wizard
#: code:addons/account_invoice_update_wizard/wizard/account_move_update.py:0
#, python-format
msgid ""
"The original payment term '%s' doesn't have the same terms (number of terms "
"and/or amount) as the new payment term '%s'. You can only switch to a "
"payment term that has the same number of terms with the same amount."
msgstr ""
#. module: account_invoice_update_wizard
#: code:addons/account_invoice_update_wizard/wizard/account_move_update.py:0
#, python-format
msgid ""
"This wizard doesn't support the update of payment terms on an invoice which "
"is partially or fully paid."
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_update__move_type
msgid "Type"
msgstr ""
#. module: account_invoice_update_wizard
#: model_terms:ir.ui.view,arch_db:account_invoice_update_wizard.account_invoice_update_form
msgid "Update"
msgstr "Mettre à jour"
#. module: account_invoice_update_wizard
#: model_terms:ir.ui.view,arch_db:account_invoice_update_wizard.view_move_form_inherit
msgid "Update Invoice"
msgstr "Mettre à jour"
#. module: account_invoice_update_wizard
#: model_terms:ir.ui.view,arch_db:account_invoice_update_wizard.account_invoice_update_form
msgid "Update Invoice Wizard"
msgstr "Assistant de mise à jour"
#. module: account_invoice_update_wizard
#: model:ir.model,name:account_invoice_update_wizard.model_account_move_line_update
msgid "Update non-legal fields of invoice lines"
msgstr "Mettre à jour les champs non légaux des lignes de facture"
#. module: account_invoice_update_wizard
#: model:ir.model.fields,field_description:account_invoice_update_wizard.field_account_move_line_update__parent_id
msgid "Wizard"
msgstr ""
#. module: account_invoice_update_wizard
#: model:ir.model,name:account_invoice_update_wizard.model_account_move_update
msgid "Wizard to update non-legal fields of invoice"
msgstr "Assistant pour mettre à jours les champs non légaux"
#~ msgid "Account"
#~ msgstr "Compte"

View File

@@ -48,6 +48,7 @@ class AccountMoveUpdate(models.TransientModel):
aa_tags = [(6, 0, aa_tags.ids)] if aa_tags else False aa_tags = [(6, 0, aa_tags.ids)] if aa_tags else False
res['line_ids'].append([0, 0, { res['line_ids'].append([0, 0, {
'invoice_line_id': line.id, 'invoice_line_id': line.id,
'sequence': line.sequence,
'name': line.name, 'name': line.name,
'quantity': line.quantity, 'quantity': line.quantity,
'price_subtotal': line.price_subtotal, 'price_subtotal': line.price_subtotal,
@@ -231,7 +232,9 @@ class AccountMoveUpdate(models.TransientModel):
class AccountMoveLineUpdate(models.TransientModel): class AccountMoveLineUpdate(models.TransientModel):
_name = 'account.move.line.update' _name = 'account.move.line.update'
_description = 'Update non-legal fields of invoice lines' _description = 'Update non-legal fields of invoice lines'
_order = "sequence, name"
sequence = fields.Integer()
parent_id = fields.Many2one( parent_id = fields.Many2one(
'account.move.update', string='Wizard', ondelete='cascade') 'account.move.update', string='Wizard', ondelete='cascade')
invoice_line_id = fields.Many2one( invoice_line_id = fields.Many2one(

View File

@@ -18,12 +18,13 @@
<field string="Bill Reference" attrs="{'invisible': [('move_type', 'not in', ('in_invoice', 'in_refund'))]}" name="ref"/> <field string="Bill Reference" attrs="{'invisible': [('move_type', 'not in', ('in_invoice', 'in_refund'))]}" name="ref"/>
<field string="Customer Reference" attrs="{'invisible': [('move_type', 'not in', ('out_invoice', 'out_refund'))]}" name="ref"/> <field string="Customer Reference" attrs="{'invisible': [('move_type', 'not in', ('out_invoice', 'out_refund'))]}" name="ref"/>
<field name="invoice_origin"/> <field name="invoice_origin"/>
<field name="invoice_payment_term_id" widget="selection"/> <!-- update of payment term is broken -->
<!-- <field name="invoice_payment_term_id" widget="selection"/>-->
<field name="partner_bank_id"/> <field name="partner_bank_id"/>
<field name="user_id" options="{'no_open': True, 'no_create': True, 'no_create_edit': True}"/> <field name="user_id" options="{'no_open': True, 'no_create': True, 'no_create_edit': True}"/>
</group> </group>
<group name="lines"> <group name="lines">
<field name="line_ids" nolabel="1"> <field name="line_ids" nolabel="1" widget="section_and_note_one2many">
<tree editable="bottom" create="false" delete="false" edit="true"> <tree editable="bottom" create="false" delete="false" edit="true">
<field name="invoice_line_id" invisible="1"/> <field name="invoice_line_id" invisible="1"/>
<field name="display_type" invisible="1"/> <field name="display_type" invisible="1"/>

View File

@@ -12,7 +12,7 @@
<field name="model">account.move.update</field> <field name="model">account.move.update</field>
<field name="inherit_id" ref="account_invoice_update_wizard.account_invoice_update_form"/> <field name="inherit_id" ref="account_invoice_update_wizard.account_invoice_update_form"/>
<field name="arch" type="xml"> <field name="arch" type="xml">
<field name="invoice_payment_term_id" position="after"> <field name="partner_bank_id" position="before">
<field name="payment_mode_filter_type_domain" invisible="1"/> <field name="payment_mode_filter_type_domain" invisible="1"/>
<field name="partner_bank_filter_type_domain" invisible="1"/> <field name="partner_bank_filter_type_domain" invisible="1"/>
<field name="bank_account_required" invisible="1"/> <field name="bank_account_required" invisible="1"/>

View File

@@ -32,7 +32,7 @@ This modules adds the following functions:
* A wizard to mark several invoices as sent at once (forward from v8) * A wizard to mark several invoices as sent at once (forward from v8)
* Default date for Account Move Reversal is now D+1 instead of today * Default date for Account Move Reversal is now D+1 instead of today
* Track more fields on invoice (see details in account.py) * Track more fields on invoice (see details in account.py)
* Add boolean fields `has_discount` and `has_attachment` on invoice * Add boolean fields `has_line_discount` and `has_attachment` on invoice
* Add button "Delete line qty = 0" on supplier invoice * Add button "Delete line qty = 0" on supplier invoice
* Cut name_get() of invoice if too long * Cut name_get() of invoice if too long
* A script for if Odoo screws up invoice attachment filename * A script for if Odoo screws up invoice attachment filename

View File

@@ -1,2 +1,3 @@
from . import models from . import models
from . import wizard from . import wizard
from .hooks import post_init_hook

View File

@@ -4,7 +4,7 @@
{ {
'name': 'Account Usability', 'name': 'Account Usability',
'version': '14.0.1.0.0', 'version': '14.0.1.1.0',
'category': 'Accounting & Finance', 'category': 'Accounting & Finance',
'license': 'AGPL-3', 'license': 'AGPL-3',
'summary': 'Small usability enhancements in account module', 'summary': 'Small usability enhancements in account module',
@@ -29,7 +29,6 @@
'views/account_tax.xml', 'views/account_tax.xml',
'views/product.xml', 'views/product.xml',
'views/res_config_settings.xml', 'views/res_config_settings.xml',
'views/res_partner.xml',
'views/res_company.xml', 'views/res_company.xml',
'views/account_report.xml', 'views/account_report.xml',
'views/account_reconcile_model.xml', 'views/account_reconcile_model.xml',
@@ -41,4 +40,5 @@
], ],
'qweb': ['static/src/xml/account_payment.xml'], 'qweb': ['static/src/xml/account_payment.xml'],
'installable': True, 'installable': True,
"post_init_hook": "post_init_hook",
} }

View File

@@ -0,0 +1,9 @@
# Copyright 2022 Akretion (https://www.akretion.com).
# @author Sébastien BEAU <sebastien.beau@akretion.com>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import SUPERUSER_ID, api
def post_init_hook(cr, registry):
env = api.Environment(cr, SUPERUSER_ID, {})
env["account.move.line"].update_matching_number()

View File

@@ -524,11 +524,6 @@ msgstr ""
msgid "Recipient Bank" msgid "Recipient Bank"
msgstr "" msgstr ""
#. module: account_usability
#: model:ir.model.fields,field_description:account_usability.field_account_move_line__reconcile_string
msgid "Reconcile"
msgstr ""
#. module: account_usability #. module: account_usability
#: model:ir.model.fields,field_description:account_usability.field_account_bank_statement_line__ref #: model:ir.model.fields,field_description:account_usability.field_account_bank_statement_line__ref
#: model:ir.model.fields,field_description:account_usability.field_account_move__ref #: model:ir.model.fields,field_description:account_usability.field_account_move__ref

View File

@@ -296,11 +296,16 @@ msgstr "Possède une pièce jointe"
#. module: account_usability #. module: account_usability
#: model:ir.model.fields,field_description:account_usability.field_account_bank_statement_line__has_discount #: model:ir.model.fields,field_description:account_usability.field_account_bank_statement_line__has_discount
#: model:ir.model.fields,field_description:account_usability.field_account_move__has_discount
#: model:ir.model.fields,field_description:account_usability.field_account_payment__has_discount #: model:ir.model.fields,field_description:account_usability.field_account_payment__has_discount
msgid "Has Discount" msgid "Has Discount"
msgstr "A une réduction" msgstr "A une réduction"
#. module: account_usability
#: model:ir.model.fields,field_description:account_usability.field_account_move__has_line_discount
msgid "Has Line Discount"
msgstr "Contient une réduction"
#. module: account_usability #. module: account_usability
#: model:ir.model.fields,field_description:account_usability.field_account_bank_statement__hide_bank_statement_balance #: model:ir.model.fields,field_description:account_usability.field_account_bank_statement__hide_bank_statement_balance
#: model:ir.model.fields,field_description:account_usability.field_account_journal__hide_bank_statement_balance #: model:ir.model.fields,field_description:account_usability.field_account_journal__hide_bank_statement_balance

View File

@@ -0,0 +1,9 @@
# Copyright 2020 ACSONE SA/NV
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import SUPERUSER_ID, api
def migrate(cr, version):
env = api.Environment(cr, SUPERUSER_ID, {})
env["account.move.line"].update_matching_number()

View File

@@ -2,12 +2,16 @@
# @author Alexis de Lattre <alexis.delattre@akretion.com> # @author Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from datetime import timedelta
import logging
from odoo import api, fields, models, _ from odoo import api, fields, models, _
from odoo.exceptions import UserError
from odoo.osv import expression
from odoo.tools import float_is_zero from odoo.tools import float_is_zero
from odoo.tools.misc import format_date from odoo.tools.misc import format_date
from odoo.osv import expression
from datetime import timedelta _logger = logging.getLogger(__name__)
from odoo.exceptions import UserError
class AccountMove(models.Model): class AccountMove(models.Model):
@@ -24,8 +28,8 @@ class AccountMove(models.Model):
fiscal_position_id = fields.Many2one(tracking=True) fiscal_position_id = fields.Many2one(tracking=True)
amount_total = fields.Monetary(tracking=True) amount_total = fields.Monetary(tracking=True)
# for invoice report # for invoice report
has_discount = fields.Boolean( has_line_discount = fields.Boolean(
compute='_compute_has_discount', readonly=True) compute='_compute_has_line_discount', readonly=True)
# has_attachment is useful for those who use attachment to archive # has_attachment is useful for those who use attachment to archive
# supplier invoices. It allows them to find supplier invoices # supplier invoices. It allows them to find supplier invoices
# that don't have any attachment # that don't have any attachment
@@ -37,15 +41,15 @@ class AccountMove(models.Model):
help="This information appear on invoice qweb report " help="This information appear on invoice qweb report "
"(you may use it for your own report)") "(you may use it for your own report)")
def _compute_has_discount(self): def _compute_has_line_discount(self):
prec = self.env['decimal.precision'].precision_get('Discount') prec = self.env['decimal.precision'].precision_get('Discount')
for inv in self: for inv in self:
has_discount = False has_line_discount = False
for line in inv.invoice_line_ids: for line in inv.invoice_line_ids:
if not line.display_type and not float_is_zero(line.discount, precision_digits=prec): if not line.display_type and not float_is_zero(line.discount, precision_digits=prec):
has_discount = True has_line_discount = True
break break
inv.has_discount = has_discount inv.has_line_discount = has_line_discount
def _compute_has_attachment(self): def _compute_has_attachment(self):
iao = self.env['ir.attachment'] iao = self.env['ir.attachment']
@@ -217,7 +221,7 @@ class AccountMove(models.Model):
if self.is_purchase_document(include_receipts=True): if self.is_purchase_document(include_receipts=True):
tax_lock_date = self.company_id.tax_lock_date tax_lock_date = self.company_id.tax_lock_date
if invoice_date and tax_lock_date and has_tax and invoice_date <= tax_lock_date: if invoice_date and tax_lock_date and has_tax and invoice_date <= tax_lock_date:
invoice_date = tax_lock_date + timedelta(days=1) invoice_date = tax_lock_date + timedelta(days=1)
date = invoice_date date = invoice_date
return date return date
@@ -238,8 +242,6 @@ class AccountMoveLine(models.Model):
full_reconcile_id = fields.Many2one(string='Full Reconcile') full_reconcile_id = fields.Many2one(string='Full Reconcile')
matched_debit_ids = fields.One2many(string='Partial Reconcile Debit') matched_debit_ids = fields.One2many(string='Partial Reconcile Debit')
matched_credit_ids = fields.One2many(string='Partial Reconcile Credit') matched_credit_ids = fields.One2many(string='Partial Reconcile Credit')
reconcile_string = fields.Char(
compute='_compute_reconcile_string', string='Reconcile', store=True)
# for optional display in tree view # for optional display in tree view
product_barcode = fields.Char(related='product_id.barcode', string="Product Barcode") product_barcode = fields.Char(related='product_id.barcode', string="Product Barcode")
@@ -255,17 +257,21 @@ class AccountMoveLine(models.Model):
}) })
return action return action
@api.depends( def update_matching_number(self):
'full_reconcile_id', 'matched_debit_ids', 'matched_credit_ids') records = self.search([("matching_number", "=", "P")])
def _compute_reconcile_string(self): _logger.info(f"Update partial reconcile number for {len(records)} lines")
for line in self: records._compute_matching_number()
rec_str = False
if line.full_reconcile_id: def _compute_matching_number(self):
rec_str = line.full_reconcile_id.name # TODO maybe it will be better to have the same maching_number for
else: # all partial so it will be easier to group by
rec_str = ', '.join([ super()._compute_matching_number()
'a%d' % pr.id for pr in line.matched_debit_ids + line.matched_credit_ids]) for record in self:
line.reconcile_string = rec_str if record.matching_number == "P":
record.matching_number = ", ".join([
"a%d" % pr.id
for pr in record.matched_debit_ids + record.matched_credit_ids
])
def _get_computed_name(self): def _get_computed_name(self):
# This is useful when you want to have the product code in a dedicated # This is useful when you want to have the product code in a dedicated

View File

@@ -13,6 +13,9 @@
<field name="model">account.bank.statement</field> <field name="model">account.bank.statement</field>
<field name="inherit_id" ref="account.view_bank_statement_form"/> <field name="inherit_id" ref="account.view_bank_statement_form"/>
<field name="arch" type="xml"> <field name="arch" type="xml">
<button name="button_reopen" position="attributes">
<attribute name="confirm">Are you sure ? Don't do 'Reset to New' if you just want to modify the bank journal entry of an existing statement line.</attribute>
</button>
<button name="button_reopen" position="after"> <button name="button_reopen" position="after">
<button <button
name="button_undo_reconciliation" name="button_undo_reconciliation"

View File

@@ -42,8 +42,7 @@
<attribute name="optional">show</attribute> <attribute name="optional">show</attribute>
</xpath> </xpath>
<xpath expr="//field[@name='line_ids']/tree/field[@name='tax_tag_ids']" position="after"> <xpath expr="//field[@name='line_ids']/tree/field[@name='tax_tag_ids']" position="after">
<field name="matching_number" optional="hide"/> <field name="matching_number" optional="show"/>
<field name="reconcile_string" optional="show"/>
</xpath> </xpath>
<xpath expr="//field[@name='invoice_line_ids']/tree/field[@name='product_id']" position="after"> <xpath expr="//field[@name='invoice_line_ids']/tree/field[@name='product_id']" position="after">
<field name="product_barcode" optional="hide"/> <field name="product_barcode" optional="hide"/>
@@ -106,7 +105,7 @@
<separator/> <separator/>
</filter> </filter>
<field name="partner_id" position="after"> <field name="partner_id" position="after">
<field name="reconcile_string" /> <field name="matching_number" />
<field name="debit" filter_domain="['|', ('debit', '=', self), ('credit', '=', self)]" string="Debit or Credit"/> <field name="debit" filter_domain="['|', ('debit', '=', self), ('credit', '=', self)]" string="Debit or Credit"/>
</field> </field>
<filter name="unreconciled" position="before"> <filter name="unreconciled" position="before">
@@ -118,6 +117,9 @@
<field name="name" position="attributes"> <field name="name" position="attributes">
<attribute name="string">Label, Reference, Account or Partner</attribute> <attribute name="string">Label, Reference, Account or Partner</attribute>
</field> </field>
<field name="name" position="before">
<field name="move_id" position="move"/>
</field>
<field name="partner_id" position="attributes"> <field name="partner_id" position="attributes">
<attribute name="domain">['|', ('parent_id', '=', False), ('is_company', '=', True)]</attribute> <attribute name="domain">['|', ('parent_id', '=', False), ('is_company', '=', True)]</attribute>
</field> </field>

View File

@@ -1,23 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2017-2020 Akretion (http://www.akretion.com/)
@author: Alexis de Lattre <alexis.delattre@akretion.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->
<odoo>
<record id="view_partner_property_form" model="ir.ui.view">
<field name="name">account_usability.res.partner.form</field>
<field name="model">res.partner</field>
<field name="inherit_id" ref="account.view_partner_property_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='bank_ids']/tree/field[@name='acc_number']" position="after">
<field name="acc_type"/>
</xpath>
</field>
</record>
</odoo>

View File

@@ -12,13 +12,14 @@ class AccountMoveReversal(models.TransientModel):
# Set default reversal date to original move + 1 day # Set default reversal date to original move + 1 day
# and raise error if original move has already been reversed # and raise error if original move has already been reversed
# WARNING: this wizard is also used to generate refunds
@api.model @api.model
def default_get(self, fields_list): def default_get(self, fields_list):
res = super().default_get(fields_list) res = super().default_get(fields_list)
assert self._context.get('active_model') == 'account.move' assert self._context.get('active_model') == 'account.move'
amo = self.env['account.move'] amo = self.env['account.move']
moves = amo.browse(self._context['active_ids']) moves = amo.browse(self._context['active_ids'])
if len(moves) == 1: if len(moves) == 1 and moves.move_type not in ('out_invoice', 'in_invoice'):
res['date'] = moves.date + relativedelta(days=1) res['date'] = moves.date + relativedelta(days=1)
reversed_move = amo.search([('reversed_entry_id', 'in', moves.ids)], limit=1) reversed_move = amo.search([('reversed_entry_id', 'in', moves.ids)], limit=1)
if reversed_move: if reversed_move:

View File

@@ -19,4 +19,10 @@
</field> </field>
</record> </record>
<!-- By default, the menu entry "CRM > Sales > My Activities" is limited to
the sales Manager. The Sale user should be able to see it -->
<record id="crm.crm_lead_menu_my_activities" model="ir.ui.menu">
<field name="groups_id" eval="[(6, 0, [])]"/>
</record>
</odoo> </odoo>

View File

@@ -1,12 +1,12 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{ {
'name': 'Developer Menu', "name": "Developer Menu",
'version': '12.0.0.0.0', "version": "14.0.0.0.0",
'category': 'Tools', "category": "Tools",
'license': 'AGPL-3', "license": "AGPL-3",
'summary': "Menu Shortcut for developer usage", "summary": "Menu Shortcut for developer usage",
'description': """ "description": """
Developer menu Developer menu
============== ==============
@@ -21,11 +21,13 @@ near `Technical` menu
This module has been written by David Béal This module has been written by David Béal
from Akretion <david.beal@akretion.com>. from Akretion <david.beal@akretion.com>.
""", """,
'author': 'Akretion', "author": "Akretion",
'website': 'http://www.akretion.com', "website": "https://www.akretion.com",
'depends': ['mail'], "depends": [
'data': [ "mail",
'menu_view.xml'
], ],
'installable': False, "data": [
"menu_view.xml",
],
"installable": True,
} }

View File

@@ -2,7 +2,7 @@
<odoo> <odoo>
<menuitem id="conf_tech" parent="base.menu_administration" name="🧰" groups="base.group_erp_manager" sequence="100"/> <menuitem id="conf_tech" parent="base.menu_administration" name="🧰" groups="base.group_erp_manager" sequence="1"/>
<menuitem id="model" name="Model" parent="conf_tech" action="base.action_model_model" sequence="10"/> <menuitem id="model" name="Model" parent="conf_tech" action="base.action_model_model" sequence="10"/>
<menuitem id="view" name="View" parent="conf_tech" action="base.action_ui_view" sequence="20" /> <menuitem id="view" name="View" parent="conf_tech" action="base.action_ui_view" sequence="20" />
<menuitem id="rec_rule" name="Record Rule" parent="conf_tech" action="base.action_rule" sequence="30" /> <menuitem id="rec_rule" name="Record Rule" parent="conf_tech" action="base.action_rule" sequence="30" />

View File

@@ -10,7 +10,7 @@ def web_m2x_options_create(cr, registry):
env = Environment(cr, SUPERUSER_ID, {}) env = Environment(cr, SUPERUSER_ID, {})
config_parameter = env['ir.config_parameter'].search( config_parameter = env['ir.config_parameter'].search(
[('key', '=', 'web_m2x_options.create')]) [('key', '=', 'web_m2x_options.create')])
if config_parameter and config_parameter.value != 'False': if config_parameter:
config_parameter.write({'value': 'False'}) config_parameter.write({'value': 'False'})
else: else:
env['ir.config_parameter'].create({ env['ir.config_parameter'].create({

View File

@@ -23,7 +23,7 @@ Small usability improvements on mails:
'website': 'http://www.akretion.com', 'website': 'http://www.akretion.com',
'depends': ['mail'], 'depends': ['mail'],
'data': [ 'data': [
#'views/mail_view.xml', 'views/mail_activity.xml',
#'data/mail_data.xml', #'data/mail_data.xml',
#'wizard/email_template_preview_view.xml', #'wizard/email_template_preview_view.xml',
#'wizard/mail_compose_message_view.xml', #'wizard/mail_compose_message_view.xml',

View File

@@ -1,2 +1,3 @@
from . import res_partner from . import res_partner
from . import mail_activity
from . import mail_template from . import mail_template

View File

@@ -0,0 +1,24 @@
# Copyright 2023 Akretion France (http://www.akretion.com).
# @author Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import fields, models
class MailActivity(models.Model):
_inherit = 'mail.activity'
res_model_name = fields.Char(related='res_model_id.name', string='Document Type')
def jump_to_record(self):
self.ensure_one()
action = {}
if self.res_id and self.res_model and self.res_name:
action.update({
'type': 'ir.actions.act_window',
'name': self.res_name,
'res_model': self.res_model,
'view_mode': 'form',
'res_id': self.res_id,
})
return action

View File

@@ -0,0 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2023 Akretion France (http://www.akretion.com/)
@author: Alexis de Lattre <alexis.delattre@akretion.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->
<odoo>
<record id="mail_activity_view_search" model="ir.ui.view">
<field name="model">mail.activity</field>
<field name="inherit_id" ref="mail.mail_activity_view_search"/>
<field name="arch" type="xml">
<filter name="activities_upcoming_all" position="after">
<separator/>
<filter string="My Activities" domain="[('user_id', '=', uid)]" name="my_activities"/>
</filter>
</field>
</record>
<record id="mail_activity_view_tree" model="ir.ui.view">
<field name="model">mail.activity</field>
<field name="inherit_id" ref="mail.mail_activity_view_tree"/>
<field name="arch" type="xml">
<field name="summary" position="after">
<field name="user_id" optional="show" widget="many2one_avatar_user"/>
</field>
<field name="res_name" position="before">
<button name="jump_to_record" type="object" attrs="{'invisible': [('res_name', '=', False)]}" icon="fa-binoculars" string="Go to source" class="text-warning" invisible="not context.get('mail_activity_main_view')"/>
</field>
<field name="res_name" position="after">
<field name="res_model_name" invisible="not context.get('mail_activity_main_view')"/>
</field>
</field>
</record>
<record id="mail_activity_view_form_popup" model="ir.ui.view">
<field name="model">mail.activity</field>
<field name="inherit_id" ref="mail.mail_activity_view_form_popup"/>
<field name="arch" type="xml">
<field name="activity_type_id" position="before">
<field name="res_name" invisible="not context.get('mail_activity_main_view')"/>
<field name="res_model_name" invisible="not context.get('mail_activity_main_view')"/>
</field>
<button name="action_close_dialog" position="before">
<button name="jump_to_record" type="object" attrs="{'invisible': [('res_id', '=', False)]}" class="btn-primary" string="Go to source" invisible="not context.get('mail_activity_main_view')"/>
</button>
</field>
</record>
</odoo>

View File

@@ -48,7 +48,7 @@ class ProductTemplate(models.Model):
_inherit = ['product.template', 'product.categ.tax.mixin'] _inherit = ['product.template', 'product.categ.tax.mixin']
_name = 'product.template' _name = 'product.template'
@api.constrains('taxes_id', 'supplier_taxes_id') @api.constrains('taxes_id', 'supplier_taxes_id', 'categ_id')
def _check_tax_categ(self): def _check_tax_categ(self):
# self.name != 'Pay Debt' is a stupid hack to avoid blocking the # self.name != 'Pay Debt' is a stupid hack to avoid blocking the
# installation of the module 'pos_debt_notebook' # installation of the module 'pos_debt_notebook'

View File

@@ -0,0 +1,2 @@
from . import models
from .post_install import set_product_detailed_type

View File

@@ -0,0 +1,19 @@
# Copyright 2023 Akretion (http://www.akretion.com)
# @author Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{
'name': 'Product Detailed Type',
'version': '14.0.1.0.0',
'category': 'Product',
'license': 'LGPL-3',
'summary': 'Backport detailed_type from v16 to v14',
'author': 'Akretion',
'website': 'https://github.com/akretion/odoo-usability',
'depends': ['product'],
'data': [
'views/product.xml',
],
'post_init_hook': 'set_product_detailed_type',
'installable': True,
}

View File

@@ -0,0 +1 @@
from . import product_template

View File

@@ -0,0 +1,25 @@
# Copyright 2023 Akretion France (http://www.akretion.com/)
# Copyright 2023 Odoo SA (contains code copy-pasted from Odoo v16)
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import api, fields, models
class ProductTemplate(models.Model):
_inherit = 'product.template'
detailed_type = fields.Selection([
('consu', 'Consumable'),
('service', 'Service'),
], string='Product Type', default='consu', required=True, tracking=True)
type = fields.Selection(compute='_compute_type', store=True, string="Type")
def _detailed_type_mapping(self):
return {}
@api.depends('detailed_type')
def _compute_type(self):
type_mapping = self._detailed_type_mapping()
for record in self:
record.type = type_mapping.get(record.detailed_type, record.detailed_type)

View File

@@ -0,0 +1,7 @@
# Copyright 2023 Akretion France (http://www.akretion.com/)
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
def set_product_detailed_type(cr, registry):
cr.execute('UPDATE product_template SET detailed_type=type')

View File

@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2023 Akretion France (http://www.akretion.com/)
@author: Alexis de Lattre <alexis.delattre@akretion.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->
<odoo>
<record id="product_template_tree_view" model="ir.ui.view">
<field name="model">product.template</field>
<field name="inherit_id" ref="product.product_template_tree_view"/>
<field name="arch" type="xml">
<field name="type" position="after">
<field name="detailed_type" optional="hide" readonly="1"/>
</field>
</field>
</record>
<record id="product_template_form_view" model="ir.ui.view">
<field name="model">product.template</field>
<field name="inherit_id" ref="product.product_template_form_view"/>
<field name="arch" type="xml">
<field name="type" position="after">
<field name="detailed_type"/>
</field>
<field name="type" position="attributes">
<attribute name="invisible">1</attribute>
</field>
</field>
</record>
<record id="product_template_search_view" model="ir.ui.view">
<field name="model">product.template</field>
<field name="inherit_id" ref="product.product_template_search_view"/>
<field name="arch" type="xml">
<filter name="type" position="attributes">
<attribute name="invisible">1</attribute>
</filter>
<filter name="type" position="after">
<filter name="detailed_type_groupby" string="Product Type" context="{'group_by': 'detailed_type'}"/>
</filter>
</field>
</record>
</odoo>

View File

@@ -0,0 +1 @@
from . import models

View File

@@ -0,0 +1,16 @@
# Copyright 2023 Akretion (http://www.akretion.com)
# @author Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{
'name': 'Product Detailed Type Stock',
'version': '14.0.1.0.0',
'category': 'Product',
'license': 'LGPL-3',
'summary': 'Glue module between product_detailed_type and stock',
'author': 'Akretion',
'website': 'https://github.com/akretion/odoo-usability',
'depends': ['product_detailed_type', 'stock'],
'installable': True,
'auto_install': True,
}

View File

@@ -0,0 +1 @@
from . import product_template

View File

@@ -0,0 +1,13 @@
# Copyright 2023 Akretion France (http://www.akretion.com/)
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import fields, models
class ProductTemplate(models.Model):
_inherit = 'product.template'
detailed_type = fields.Selection(selection_add=[
('product', 'Storable Product')
], ondelete={'product': 'set default'})

View File

@@ -41,6 +41,7 @@ This module has been written by Alexis de Lattre from Akretion
'barcodes', 'barcodes',
'base_report_to_printer', 'base_report_to_printer',
], ],
'external_dependencies': {'python': ['python-barcode>=0.14.0']},
'data': [ 'data': [
'security/ir.model.access.csv', 'security/ir.model.access.csv',
'wizard/product_print_zpl_barcode_view.xml', 'wizard/product_print_zpl_barcode_view.xml',

View File

@@ -1,11 +1,14 @@
# -*- coding: utf-8 -*- # Copyright 2020-2023 Akretion France (http://www.akretion.com/)
# Copyright 2020 Akretion France (http://www.akretion.com/)
# @author: Alexis de Lattre <alexis.delattre@akretion.com> # @author: Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import api, fields, models, _ from odoo import api, fields, models, _
from odoo.exceptions import UserError from odoo.exceptions import UserError
from stdnum.ean import calc_check_digit from stdnum.ean import calc_check_digit, is_valid
from barcode import EAN13, EAN8
from barcode.writer import ImageWriter, SVGWriter
import base64
import io
class ProductTemplate(models.Model): class ProductTemplate(models.Model):
@@ -34,8 +37,8 @@ class ProductTemplate(models.Model):
"print_zpl_barcode_from_product_template on product '%s' " "print_zpl_barcode_from_product_template on product '%s' "
"because it has %d variants and not just one.") "because it has %d variants and not just one.")
% (self.display_name, self.product_variant_count)) % (self.display_name, self.product_variant_count))
action = self.env.ref( action = self.env["ir.actions.actions"]._for_xml_id(
'product_print_zpl_barcode.product_print_zpl_barcode_action').sudo().read()[0] 'product_print_zpl_barcode.product_print_zpl_barcode_action')
action['context'] = { action['context'] = {
'active_id': self.product_variant_ids[0].id, 'active_id': self.product_variant_ids[0].id,
'active_model': 'product.product', 'active_model': 'product.product',
@@ -46,6 +49,50 @@ class ProductTemplate(models.Model):
class ProductProduct(models.Model): class ProductProduct(models.Model):
_inherit = 'product.product' _inherit = 'product.product'
# Not useful for ZPL, but it is often useful to have a barcode image field
barcode_image_png = fields.Binary(
compute='_compute_barcode_image_png',
string='PNG Barcode Image')
barcode_image_svg = fields.Binary(
compute='_compute_barcode_image_svg',
string='SVG Barcode Image')
def _get_barcode_image(self, img_format):
self.ensure_one()
barcode = self.barcode
if not barcode:
return False
res = False
if isinstance(barcode, str) and len(barcode) in (8, 13) and is_valid(barcode):
barcode_obj = False
if img_format == 'svg':
writer = SVGWriter()
elif img_format == 'png':
writer = ImageWriter()
else:
return False
if len(barcode) == 13:
barcode_obj = EAN13(barcode, writer=writer, guardbar=True)
elif len(barcode) == 8:
barcode_obj = EAN8(barcode, writer=writer, guardbar=True)
if barcode_obj:
barcode_file = io.BytesIO()
barcode_obj.write(barcode_file)
barcode_file.seek(0)
barcode_img = barcode_file.read()
res = base64.b64encode(barcode_img)
return res
@api.depends('barcode')
def _compute_barcode_image_svg(self):
for product in self:
product.barcode_image_svg = product._get_barcode_image('svg')
@api.depends('barcode')
def _compute_barcode_image_png(self):
for product in self:
product.barcode_image_png = product._get_barcode_image('png')
def generate_barcode_from_product_product(self): def generate_barcode_from_product_product(self):
self.ensure_one() self.ensure_one()
if self.barcode: if self.barcode:

View File

@@ -5,7 +5,7 @@
from odoo import api, fields, models, _ from odoo import api, fields, models, _
from odoo.exceptions import UserError from odoo.exceptions import UserError
from odoo.tools import float_compare, float_is_zero from odoo.tools import float_compare, float_is_zero
from stdnum.ean import is_valid from stdnum.ean import is_valid, calc_check_digit
import base64 import base64
import re import re
@@ -55,8 +55,7 @@ class ProductPrintZplBarcode(models.TransientModel):
product_id = fields.Many2one( product_id = fields.Many2one(
'product.product', string='Product', required=True, readonly=True) 'product.product', string='Product', required=True, readonly=True)
uom_id = fields.Many2one( uom_id = fields.Many2one(related='product_id.uom_id')
related='product_id.uom_id', readonly=True)
# 1 line = un peu moins de 30 # 1 line = un peu moins de 30
product_name = fields.Char('Product Label', required=True, size=56) product_name = fields.Char('Product Label', required=True, size=56)
nomenclature_id = fields.Many2one( nomenclature_id = fields.Many2one(
@@ -64,15 +63,13 @@ class ProductPrintZplBarcode(models.TransientModel):
rule_id = fields.Many2one( rule_id = fields.Many2one(
'barcode.rule', string='Barcode Rule', readonly=True, 'barcode.rule', string='Barcode Rule', readonly=True,
compute='_compute_rule_id') compute='_compute_rule_id')
barcode_type = fields.Selection( barcode_type = fields.Selection(related='rule_id.type', string="Barcode Type")
related='rule_id.type', readonly=True, string="Barcode Type")
label_size = fields.Selection([ label_size = fields.Selection([
('38x25', '38x25 mm'), ('38x25', '38x25 mm'),
], required=True, default='38x25', string='Label Size') ], required=True, default='38x25')
pricelist_id = fields.Many2one( pricelist_id = fields.Many2one(
'product.pricelist', string='Pricelist', required=True) 'product.pricelist', string='Pricelist', required=True)
currency_id = fields.Many2one( currency_id = fields.Many2one(related='pricelist_id.currency_id')
related='pricelist_id.currency_id', readonly=True)
# TODO: for the moment, we only support weight, but... # TODO: for the moment, we only support weight, but...
quantity = fields.Float(digits='Stock Weight') quantity = fields.Float(digits='Stock Weight')
price_uom = fields.Monetary( price_uom = fields.Monetary(
@@ -140,14 +137,14 @@ class ProductPrintZplBarcode(models.TransientModel):
"of the barcode pattern (%s).") "of the barcode pattern (%s).")
% (pbarcode, len(pbarcode), len(prefix), prefix)) % (pbarcode, len(pbarcode), len(prefix), prefix))
barcode = pbarcode[0:len(prefix)] barcode = pbarcode[0:len(prefix)]
# print "barcode=", barcode # print("barcode=", barcode)
# print "pattern=", pattern # print("pattern=", pattern)
m = re.search('\{N+D+\}', pattern) m = re.search('\{N+D+\}', pattern)
# print "m=", m # print("m=", m)
assert m assert m
pattern_val = m.group(0) pattern_val = m.group(0)
pattern_val = pattern_val[1:-1] pattern_val = pattern_val[1:-1]
# print "pattern_val=", pattern_val # print("pattern_val=", pattern_val)
max_value = 10**pattern_val.count('N') max_value = 10**pattern_val.count('N')
if float_compare(value, max_value, precision_digits=prec) != -1: if float_compare(value, max_value, precision_digits=prec) != -1:
raise UserError(_( raise UserError(_(
@@ -166,11 +163,15 @@ class ProductPrintZplBarcode(models.TransientModel):
value_d_ext = value_d + '0' * pattern_val.count('D') value_d_ext = value_d + '0' * pattern_val.count('D')
# 2) cut at the right size # 2) cut at the right size
barcode += value_d_ext[0:pattern_val.count('D')] barcode += value_d_ext[0:pattern_val.count('D')]
# print "barcode=", barcode # print("barcode=", barcode)
# Add checksum # Add checksum
if self.rule_id.encoding == 'ean13': if self.rule_id.encoding == 'ean13':
barcode = bno.sanitize_ean(barcode) # I don't call bno.sanitize_ean() due to this bug:
# print "barcode FINAL=", barcode # https://github.com/odoo/odoo/pull/114112
barcode = barcode + calc_check_digit(barcode)
assert len(barcode) == 13
assert is_valid(barcode)
# print("barcode FINAL=", barcode)
zpl_unicode = self._price_weight_barcode_type_zpl() % { zpl_unicode = self._price_weight_barcode_type_zpl() % {
'product_name': self.product_name, 'product_name': self.product_name,
'ean_zpl_command': len(self.barcode) == 8 and 'B8' or 'BE', 'ean_zpl_command': len(self.barcode) == 8 and 'B8' or 'BE',
@@ -270,7 +271,8 @@ class ProductPrintZplBarcode(models.TransientModel):
'zpl_filename': 'barcode_%s.zpl' % vals['barcode'], 'zpl_filename': 'barcode_%s.zpl' % vals['barcode'],
}) })
self.write(vals) self.write(vals)
action = self.env.ref('product_print_zpl_barcode.product_print_zpl_barcode_action').sudo().read()[0] action = self.env["ir.actions.actions"]._for_xml_id(
'product_print_zpl_barcode.product_print_zpl_barcode_action')
action.update({ action.update({
'res_id': self.id, 'res_id': self.id,
'context': self._context, 'context': self._context,
@@ -285,7 +287,8 @@ class ProductPrintZplBarcode(models.TransientModel):
self.zpl_filename, base64.decodebytes(self.zpl_file), format='raw') self.zpl_filename, base64.decodebytes(self.zpl_file), format='raw')
action = True action = True
if self._context.get('print_and_new'): if self._context.get('print_and_new'):
action = self.env.ref('product_print_zpl_barcode.product_print_zpl_barcode_action').sudo().read()[0] action = self.env["ir.actions.actions"]._for_xml_id(
'product_print_zpl_barcode.product_print_zpl_barcode_action')
action.update({ action.update({
'views': False, 'views': False,
'context': self._context, 'context': self._context,

View File

@@ -0,0 +1 @@
from . import models

View File

@@ -0,0 +1,26 @@
# Copyright 2023 Akretion France (http://www.akretion.com)
# @author Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{
'name': 'Product Priority Star',
'version': '14.0.1.0.0',
'category': 'Product',
'license': 'AGPL-3',
'summary': 'Add a priority star on product',
'description': """
Product Priority Star
=====================
This module adds a priority star on products (like on pickings and manufacturing order). If the star is yellow, the product will be displayed at the top.
This module has been written by Alexis de Lattre from Akretion <alexis.delattre@akretion.com>.
""",
'author': 'Akretion',
'website': 'https://github.com/akretion/odoo-usability',
'depends': ['product'],
'excludes': ['product_priority'],
'data': ['views/product_template.xml'],
'installable': True,
}

View File

@@ -0,0 +1 @@
from . import product

View File

@@ -0,0 +1,20 @@
# Copyright 2023 Akretion France (http://www.akretion.com/)
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import fields, models
class ProductTemplate(models.Model):
_inherit = 'product.template'
_order = "priority desc, name, id"
priority = fields.Selection([
('0', 'Normal'),
('1', 'Top List'),
], default='0', index=True)
class ProductProduct(models.Model):
_inherit = 'product.product'
_order = 'priority desc, default_code, name, id'

View File

@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2023 Akretion France (http://www.akretion.com/)
@author: Alexis de Lattre <alexis.delattre@akretion.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->
<odoo>
<record id="product_template_tree_view" model="ir.ui.view">
<field name="model">product.template</field>
<field name="inherit_id" ref="product.product_template_tree_view"/>
<field name="arch" type="xml">
<field name="sequence" position="after">
<field name="priority" widget="priority" optional="show" nolabel="1"/>
</field>
</field>
</record>
<record id="product_template_form_view" model="ir.ui.view">
<field name="model">product.template</field>
<field name="inherit_id" ref="product.product_template_form_view"/>
<field name="arch" type="xml">
<field name="name" position="before">
<field name="priority" widget="priority" class="mr-3"/>
</field>
</field>
</record>
<record id="product_template_kanban_view" model="ir.ui.view">
<field name="model">product.template</field>
<field name="inherit_id" ref="product.product_template_kanban_view"/>
<field name="arch" type="xml">
<field name="lst_price" position="after">
<field name="priority" widget="priority"/>
</field>
</field>
</record>
</odoo>

View File

@@ -9,4 +9,4 @@ from odoo import fields, models
class ProductSupplierinfo(models.Model): class ProductSupplierinfo(models.Model):
_inherit = 'product.supplierinfo' _inherit = 'product.supplierinfo'
name = fields.Many2one(domain=[('parent_id', '=', False)]) name = fields.Many2one(domain=[('parent_id', '=', False)], ondelete='restrict')

View File

@@ -30,10 +30,6 @@
<field name="date_approve" position="after"> <field name="date_approve" position="after">
<field name="origin"/> <field name="origin"/>
</field> </field>
<!-- Remove once this PR is merged https://github.com/odoo/odoo/pull/35073 -->
<xpath expr="//field[@name='order_line']/form//field[@name='analytic_tag_ids']" position="attributes">
<attribute name="groups">analytic.group_analytic_tags</attribute>
</xpath>
<xpath expr="//field[@name='order_line']/tree//field[@name='product_id']" position="after"> <xpath expr="//field[@name='order_line']/tree//field[@name='product_id']" position="after">
<field name="product_supplier_code" optional="hide"/> <field name="product_supplier_code" optional="hide"/>
<field name="product_barcode" optional="hide"/> <field name="product_barcode" optional="hide"/>

View File

@@ -0,0 +1 @@
from . import models

View File

@@ -0,0 +1,18 @@
# Copyright 2023 Akretion (http://www.akretion.com)
# @author Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{
'name': 'Sale CRM Usability',
'version': '14.0.1.0.0',
'category': 'CRM',
'license': 'AGPL-3',
'summary': 'Usability improvements on sale_crm module',
'author': 'Akretion',
'website': 'https://github.com/akretion/odoo-usability',
'depends': [
'sale_crm',
],
'data': [],
'installable': True,
}

View File

@@ -0,0 +1 @@
from . import crm_lead

View File

@@ -0,0 +1,14 @@
# Copyright 2023 Akretion France (http://www.akretion.com/)
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import api, models
class CrmLead(models.Model):
_inherit = 'crm.lead'
def action_view_sale_quotation(self):
action = super().action_view_sale_quotation()
if 'search_default_partner_id' in action['context']:
action['context'].pop('search_default_partner_id')
return action

View File

@@ -1,10 +1,10 @@
# Copyright 2016-2019 Akretion (http://www.akretion.com) # Copyright 2016-2022 Akretion (http://www.akretion.com)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
# @author Alexis de Lattre <alexis.delattre@akretion.com> # @author Alexis de Lattre <alexis.delattre@akretion.com>
{ {
'name': 'Sale Quotation Title', 'name': 'Sale Quotation Title',
'version': '12.0.1.0.0', 'version': '14.0.1.0.0',
'category': 'Sales', 'category': 'Sales',
'license': 'AGPL-3', 'license': 'AGPL-3',
'summary': 'Adds a title field on quotations', 'summary': 'Adds a title field on quotations',
@@ -21,5 +21,5 @@ This module has been written by Alexis de Lattre from Akretion
'website': 'http://www.akretion.com', 'website': 'http://www.akretion.com',
'depends': ['sale'], 'depends': ['sale'],
'data': ['sale_view.xml'], 'data': ['sale_view.xml'],
'installable': False, 'installable': True,
} }

View File

@@ -1,11 +1,11 @@
# Copyright 2016-2019 Akretion (http://www.akretion.com) # Copyright 2016-2022 Akretion (http://www.akretion.com)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
# @author Alexis de Lattre <alexis.delattre@akretion.com> # @author Alexis de Lattre <alexis.delattre@akretion.com>
from odoo import models, fields from odoo import fields, models
class SaleOrder(models.Model): class SaleOrder(models.Model):
_inherit = 'sale.order' _inherit = 'sale.order'
quotation_title = fields.Char(string="Quotation Title") quotation_title = fields.Char()

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- <!--
Copyright 2016-2019 Akretion (http://www.akretion.com/) Copyright 2016-2022 Akretion (http://www.akretion.com/)
@author Alexis de Lattre <alexis.delattre@akretion.com> @author Alexis de Lattre <alexis.delattre@akretion.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
--> -->
@@ -14,7 +14,7 @@
<field name="inherit_id" ref="sale.view_order_tree"/> <field name="inherit_id" ref="sale.view_order_tree"/>
<field name="arch" type="xml"> <field name="arch" type="xml">
<field name="user_id" position="before"> <field name="user_id" position="before">
<field name="quotation_title"/> <field name="quotation_title" optional="show"/>
</field> </field>
</field> </field>
</record> </record>
@@ -25,7 +25,7 @@
<field name="inherit_id" ref="sale.view_quotation_tree"/> <field name="inherit_id" ref="sale.view_quotation_tree"/>
<field name="arch" type="xml"> <field name="arch" type="xml">
<field name="user_id" position="before"> <field name="user_id" position="before">
<field name="quotation_title"/> <field name="quotation_title" optional="show"/>
</field> </field>
</field> </field>
</record> </record>

View File

@@ -0,0 +1 @@
from . import models

View File

@@ -0,0 +1,25 @@
# Copyright 2022 Akretion (https://www.akretion.com).
# @author Sébastien BEAU <sebastien.beau@akretion.com>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
{
"name": "Sale Show Transaction",
"summary": "Make transaction hyper visible on sale order",
"version": "14.0.1.0.0",
"development_status": "Alpha",
"category": "Uncategorized",
"website": "www.akretion.com",
"author": " Akretion",
"license": "AGPL-3",
"external_dependencies": {
"python": [],
"bin": [],
},
"depends": [
"sale",
],
"data": [
"views/sale_order_view.xml",
],
"demo": [
],
}

View File

@@ -0,0 +1 @@
from . import sale_order

View File

@@ -0,0 +1,30 @@
# Copyright 2022 Akretion (https://www.akretion.com).
# @author Sébastien BEAU <sebastien.beau@akretion.com>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import _, api, fields, models
class SaleOrder(models.Model):
_inherit = 'sale.order'
main_acquirer_id = fields.Many2one(
'payment.acquirer',
'Online payment mode',
compute="_compute_main_acquirer",
store=True)
@api.depends("transaction_ids.state")
def _compute_main_acquirer(self):
for record in self:
if len(record.transaction_ids.acquirer_id) > 1:
for state in ["done", "authorized", "pending", "draft", "cancel", "error"]:
transaction = record.transaction_ids.filtered(lambda s: s.state == state)
if len(transaction.acquirer_id) > 1:
transaction.sorted("amount")
if transaction:
record.main_acquirer_id = transaction[0].acquirer_id
break
else:
record.main_acquirer_id = record.transaction_ids.acquirer_id

View File

@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<record id="sale_order_view_form" model="ir.ui.view">
<field name="model">sale.order</field>
<field name="inherit_id" ref="sale.view_order_form" />
<field name="arch" type="xml">
<field name="note" position="after">
<separator string="Transaction" colspan="4"/>
<field name="transaction_ids" nolabel="1" colspan="4">
<tree
decoration-danger="state in ('error', 'cancel')"
decoration-success="state == 'done'"
>
<field name="reference"/>
<field name="create_date"/>
<field name="acquirer_id"/>
<field name="amount"/>
<field name="state"/>
</tree>
</field>
</field>
</field>
</record>
<record id="view_order_tree" model="ir.ui.view">
<field name="model">sale.order</field>
<field name="inherit_id" ref="sale.view_order_tree" />
<field name="arch" type="xml">
<field name="state" position="after">
<field name="main_acquirer_id"/>
</field>
</field>
</record>
<record id="view_quotation_tree" model="ir.ui.view">
<field name="model">sale.order</field>
<field name="inherit_id" ref="sale.view_quotation_tree" />
<field name="arch" type="xml">
<field name="currency_id" position="after">
<field name="main_acquirer_id"/>
</field>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,20 @@
Module Stock Quant Package Moving Wizard
=========================================
This module provides a **super fast** and **super easy** way of moving a quant to another stock location.
Usage
=====
1. Select one or several quants
2. Click on the *Move* button
3. Follow the instructions on the wizard: select a picking type (or a destination stock location) and, if you don't want to move the full quant, edit the quantity to move.
4. Validate the wizard
Contributors
============
* Alexis de Lattre <alexis.delattre@akretion.com>
* Oihane Crucelaegui <oihanecrucelaegi@avanzosc.es>
* Pedro M. Baeza <pedro.baeza@serviciosbaeza.com>
* Ana Juaristi <ajuaristio@gmail.com>

View File

@@ -0,0 +1,2 @@
from . import models
from . import wizards

View File

@@ -0,0 +1,29 @@
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html
{
"name": "Quant & Package moving wizard",
"summary": "Select a quant Move quants to another location in a few clicks",
"category": "Inventory/Inventory",
"version": "14.0.1.0.0",
"license": "AGPL-3",
"depends": ["stock"],
"author": "AvanzOSC, "
"Serv. Tecnol. Avanzados - Pedro M. Baeza, "
"Akretion",
"maintainers": ["alexis-via"],
"website": "https://github.com/akretion/odoo-usability",
"contributors": [
"Oihane Crucelaegui <oihanecrucelaegi@avanzosc.es>",
"Pedro M. Baeza <pedro.baeza@serviciosbaeza.com>",
"Ana Juaristi <ajuaristio@gmail.com>",
"Alexis de Lattre <alexis.delattre@akretion.com>",
],
"data": [
# "wizards/quants_move_wizard_view.xml",
"security/ir.model.access.csv",
"wizards/stock_quant_move_wizard_view.xml",
"views/stock_quant.xml",
# "views/stock_quant_package.xml",
],
"installable": True,
}

View File

@@ -0,0 +1,223 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * stock_quant_package_move_wizard
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-02-13 21:59+0000\n"
"PO-Revision-Date: 2023-02-13 21:59+0000\n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: stock_quant_package_move_wizard
#: model_terms:ir.ui.view,arch_db:stock_quant_package_move_wizard.stock_quant_move_wizard_form
msgid "Cancel"
msgstr "Annuler"
#. module: stock_quant_package_move_wizard
#. odoo-python
#: code:addons/stock_quant_package_move_wizard/models/stock_quant.py:0
#, python-format
msgid "Cannot move to '%s' which is a view location."
msgstr "Impossible de déplacer vers '%s' qui est un emplacement de type Vue."
#. module: stock_quant_package_move_wizard
#: model:ir.model.fields,field_description:stock_quant_package_move_wizard.field_stock_quant_move_wizard__company_id
msgid "Company"
msgstr "Société"
#. module: stock_quant_package_move_wizard
#: model:ir.model.fields,field_description:stock_quant_package_move_wizard.field_stock_quant_move_wizard__create_uid
#: model:ir.model.fields,field_description:stock_quant_package_move_wizard.field_stock_quant_move_wizard_line__create_uid
msgid "Created by"
msgstr ""
#. module: stock_quant_package_move_wizard
#: model:ir.model.fields,field_description:stock_quant_package_move_wizard.field_stock_quant_move_wizard__create_date
#: model:ir.model.fields,field_description:stock_quant_package_move_wizard.field_stock_quant_move_wizard_line__create_date
msgid "Created on"
msgstr ""
#. module: stock_quant_package_move_wizard
#: model:ir.model.fields,field_description:stock_quant_package_move_wizard.field_stock_quant_move_wizard_line__src_location_id
msgid "Current Location"
msgstr "Emplacement actuel"
#. module: stock_quant_package_move_wizard
#: model:ir.model.fields,help:stock_quant_package_move_wizard.field_stock_quant_move_wizard_line__uom_id
msgid "Default unit of measure used for all stock operations."
msgstr ""
#. module: stock_quant_package_move_wizard
#: model:ir.model.fields,field_description:stock_quant_package_move_wizard.field_stock_quant_move_wizard__location_dest_id
msgid "Destination Location"
msgstr "Emplacement destination"
#. module: stock_quant_package_move_wizard
#: model:ir.model.fields,field_description:stock_quant_package_move_wizard.field_stock_quant_move_wizard__display_name
#: model:ir.model.fields,field_description:stock_quant_package_move_wizard.field_stock_quant_move_wizard_line__display_name
msgid "Display Name"
msgstr ""
#. module: stock_quant_package_move_wizard
#: model:ir.model.fields,field_description:stock_quant_package_move_wizard.field_stock_quant_move_wizard__id
#: model:ir.model.fields,field_description:stock_quant_package_move_wizard.field_stock_quant_move_wizard_line__id
msgid "ID"
msgstr ""
#. module: stock_quant_package_move_wizard
#: model:ir.model.fields,field_description:stock_quant_package_move_wizard.field_stock_quant_move_wizard____last_update
#: model:ir.model.fields,field_description:stock_quant_package_move_wizard.field_stock_quant_move_wizard_line____last_update
msgid "Last Modified on"
msgstr ""
#. module: stock_quant_package_move_wizard
#: model:ir.model.fields,field_description:stock_quant_package_move_wizard.field_stock_quant_move_wizard__write_uid
#: model:ir.model.fields,field_description:stock_quant_package_move_wizard.field_stock_quant_move_wizard_line__write_uid
msgid "Last Updated by"
msgstr ""
#. module: stock_quant_package_move_wizard
#: model:ir.model.fields,field_description:stock_quant_package_move_wizard.field_stock_quant_move_wizard__write_date
#: model:ir.model.fields,field_description:stock_quant_package_move_wizard.field_stock_quant_move_wizard_line__write_date
msgid "Last Updated on"
msgstr ""
#. module: stock_quant_package_move_wizard
#: model:ir.model.fields,field_description:stock_quant_package_move_wizard.field_stock_quant_move_wizard__line_ids
msgid "Lines"
msgstr "Lignes"
#. module: stock_quant_package_move_wizard
#: model:ir.model,name:stock_quant_package_move_wizard.model_stock_quant_move_wizard_line
msgid "Lines of the wizard to move quants"
msgstr "Lignes de l'assistant de déplacement des quants"
#. module: stock_quant_package_move_wizard
#: model:ir.model.fields,field_description:stock_quant_package_move_wizard.field_stock_quant_move_wizard_line__lot_id
msgid "Lot/Serial Number"
msgstr "Lot/numéro de série"
#. module: stock_quant_package_move_wizard
#: model_terms:ir.ui.view,arch_db:stock_quant_package_move_wizard.stock_quant_move_wizard_form
msgid "Move"
msgstr "Déplacer"
#. module: stock_quant_package_move_wizard
#: model:ir.actions.act_window,name:stock_quant_package_move_wizard.stock_quant_move_wizard_action
msgid "Move to Another Location"
msgstr "Déplacer vers un autre emplacement"
#. module: stock_quant_package_move_wizard
#: model:ir.model.fields,field_description:stock_quant_package_move_wizard.field_stock_quant_move_wizard_line__quant_quantity
msgid "On Hand Qty"
msgstr "Qté en stock"
#. module: stock_quant_package_move_wizard
#: model:ir.model.fields,field_description:stock_quant_package_move_wizard.field_stock_quant_move_wizard__picking_type_id
msgid "Picking Type"
msgstr "Type d'opération"
#. module: stock_quant_package_move_wizard
#: model:ir.model.fields,field_description:stock_quant_package_move_wizard.field_stock_quant_move_wizard_line__product_id
msgid "Product"
msgstr "Article"
#. module: stock_quant_package_move_wizard
#: model:ir.model.fields,field_description:stock_quant_package_move_wizard.field_stock_quant_move_wizard_line__quantity
msgid "Qty to Move"
msgstr "Qté à déplacer"
#. module: stock_quant_package_move_wizard
#: model:ir.model.fields,field_description:stock_quant_package_move_wizard.field_stock_quant_move_wizard_line__quant_id
msgid "Quant"
msgstr "Quant"
#. module: stock_quant_package_move_wizard
#: model:ir.model.fields,field_description:stock_quant_package_move_wizard.field_stock_quant_move_wizard_line__wizard_id
msgid "Quant Move"
msgstr "Déplacer Quant"
#. module: stock_quant_package_move_wizard
#: model:ir.model.fields,help:stock_quant_package_move_wizard.field_stock_quant_move_wizard_line__quant_quantity
msgid ""
"Quantity of products in this quant, in the default unit of measure of the "
"product"
msgstr ""
#. module: stock_quant_package_move_wizard
#: model:ir.model,name:stock_quant_package_move_wizard.model_stock_quant
msgid "Quants"
msgstr "Quants"
#. module: stock_quant_package_move_wizard
#: model:ir.model.fields,field_description:stock_quant_package_move_wizard.field_stock_quant_move_wizard__origin
msgid "Source Document"
msgstr "Document source"
#. module: stock_quant_package_move_wizard
#: model_terms:ir.ui.view,arch_db:stock_quant_package_move_wizard.stock_quant_move_wizard_form
msgid "Unit"
msgstr "Unité"
#. module: stock_quant_package_move_wizard
#: model:ir.model.fields,field_description:stock_quant_package_move_wizard.field_stock_quant_move_wizard_line__uom_id
msgid "Unit of Measure"
msgstr "Unité de mesure"
#. module: stock_quant_package_move_wizard
#: model:ir.model,name:stock_quant_package_move_wizard.model_stock_quant_move_wizard
msgid "Wizard to Move Quants"
msgstr "Assistant de déplacement de quants"
#. module: stock_quant_package_move_wizard
#. odoo-python
#: code:addons/stock_quant_package_move_wizard/models/stock_quant.py:0
#, python-format
msgid ""
"You are trying to move %(qty)s %(uom)s of a quant of product "
"%(product_name)s that has a quantity of %(quant_quantity)s %(uom)s."
msgstr ""
"Vous essayez de déplacer %(qty)s %(uom)s d'un quant de l'article "
"%(product_name)s dont la quantité est de %(quant_quantity)s %(uom)s."
#. module: stock_quant_package_move_wizard
#. odoo-python
#: code:addons/stock_quant_package_move_wizard/models/stock_quant.py:0
#, python-format
msgid ""
"You are trying to move %(qty)s %(uom)s of a quant of product "
"%(product_name)s to %(dest_location)s, but it is already on that location!"
msgstr ""
"Vous essayez de déplacer %(qty)s %(uom)s d'un quant de l'article "
"%(product_name)s vers %(dest_location)s, mais il est déjà sur cet emplacement !"
#. module: stock_quant_package_move_wizard
#. odoo-python
#: code:addons/stock_quant_package_move_wizard/models/stock_quant.py:0
#, python-format
msgid ""
"You are trying to move %(qty)s %(uom)s of a quant of product "
"%(product_name)s, but that quant has a negative quantity (%(quant_quantity)s"
" %(uom)s)."
msgstr ""
"Vous essayez de déplacer %(qty)s %(uom)s d'un quant de l'article "
"%(product_name)s, mais ce quant est en quantité négative (%(quant_quantity)s"
" %(uom)s)."
#. module: stock_quant_package_move_wizard
#. odoo-python
#: code:addons/stock_quant_package_move_wizard/models/stock_quant.py:0
#, python-format
msgid ""
"You are trying to move %(qty)s %(uom)s of a quant of product "
"%(product_name)s: the quantity to move must be strictly positive."
msgstr ""
"Vous essayez de déplacer %(qty)s %(uom)s d'un quant de l'article "
"%(product_name)s : la quantité à déplacer doit être strictement positive."

View File

@@ -0,0 +1,214 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * stock_quant_package_move_wizard
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-02-13 21:58+0000\n"
"PO-Revision-Date: 2023-02-13 21:58+0000\n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: stock_quant_package_move_wizard
#: model_terms:ir.ui.view,arch_db:stock_quant_package_move_wizard.stock_quant_move_wizard_form
msgid "Cancel"
msgstr ""
#. module: stock_quant_package_move_wizard
#. odoo-python
#: code:addons/stock_quant_package_move_wizard/models/stock_quant.py:0
#, python-format
msgid "Cannot move to '%s' which is a view location."
msgstr ""
#. module: stock_quant_package_move_wizard
#: model:ir.model.fields,field_description:stock_quant_package_move_wizard.field_stock_quant_move_wizard__company_id
msgid "Company"
msgstr ""
#. module: stock_quant_package_move_wizard
#: model:ir.model.fields,field_description:stock_quant_package_move_wizard.field_stock_quant_move_wizard__create_uid
#: model:ir.model.fields,field_description:stock_quant_package_move_wizard.field_stock_quant_move_wizard_line__create_uid
msgid "Created by"
msgstr ""
#. module: stock_quant_package_move_wizard
#: model:ir.model.fields,field_description:stock_quant_package_move_wizard.field_stock_quant_move_wizard__create_date
#: model:ir.model.fields,field_description:stock_quant_package_move_wizard.field_stock_quant_move_wizard_line__create_date
msgid "Created on"
msgstr ""
#. module: stock_quant_package_move_wizard
#: model:ir.model.fields,field_description:stock_quant_package_move_wizard.field_stock_quant_move_wizard_line__src_location_id
msgid "Current Location"
msgstr ""
#. module: stock_quant_package_move_wizard
#: model:ir.model.fields,help:stock_quant_package_move_wizard.field_stock_quant_move_wizard_line__uom_id
msgid "Default unit of measure used for all stock operations."
msgstr ""
#. module: stock_quant_package_move_wizard
#: model:ir.model.fields,field_description:stock_quant_package_move_wizard.field_stock_quant_move_wizard__location_dest_id
msgid "Destination Location"
msgstr ""
#. module: stock_quant_package_move_wizard
#: model:ir.model.fields,field_description:stock_quant_package_move_wizard.field_stock_quant_move_wizard__display_name
#: model:ir.model.fields,field_description:stock_quant_package_move_wizard.field_stock_quant_move_wizard_line__display_name
msgid "Display Name"
msgstr ""
#. module: stock_quant_package_move_wizard
#: model:ir.model.fields,field_description:stock_quant_package_move_wizard.field_stock_quant_move_wizard__id
#: model:ir.model.fields,field_description:stock_quant_package_move_wizard.field_stock_quant_move_wizard_line__id
msgid "ID"
msgstr ""
#. module: stock_quant_package_move_wizard
#: model:ir.model.fields,field_description:stock_quant_package_move_wizard.field_stock_quant_move_wizard____last_update
#: model:ir.model.fields,field_description:stock_quant_package_move_wizard.field_stock_quant_move_wizard_line____last_update
msgid "Last Modified on"
msgstr ""
#. module: stock_quant_package_move_wizard
#: model:ir.model.fields,field_description:stock_quant_package_move_wizard.field_stock_quant_move_wizard__write_uid
#: model:ir.model.fields,field_description:stock_quant_package_move_wizard.field_stock_quant_move_wizard_line__write_uid
msgid "Last Updated by"
msgstr ""
#. module: stock_quant_package_move_wizard
#: model:ir.model.fields,field_description:stock_quant_package_move_wizard.field_stock_quant_move_wizard__write_date
#: model:ir.model.fields,field_description:stock_quant_package_move_wizard.field_stock_quant_move_wizard_line__write_date
msgid "Last Updated on"
msgstr ""
#. module: stock_quant_package_move_wizard
#: model:ir.model.fields,field_description:stock_quant_package_move_wizard.field_stock_quant_move_wizard__line_ids
msgid "Lines"
msgstr ""
#. module: stock_quant_package_move_wizard
#: model:ir.model,name:stock_quant_package_move_wizard.model_stock_quant_move_wizard_line
msgid "Lines of the wizard to move quants"
msgstr ""
#. module: stock_quant_package_move_wizard
#: model:ir.model.fields,field_description:stock_quant_package_move_wizard.field_stock_quant_move_wizard_line__lot_id
msgid "Lot/Serial Number"
msgstr ""
#. module: stock_quant_package_move_wizard
#: model_terms:ir.ui.view,arch_db:stock_quant_package_move_wizard.stock_quant_move_wizard_form
msgid "Move"
msgstr ""
#. module: stock_quant_package_move_wizard
#: model:ir.actions.act_window,name:stock_quant_package_move_wizard.stock_quant_move_wizard_action
msgid "Move to Another Location"
msgstr ""
#. module: stock_quant_package_move_wizard
#: model:ir.model.fields,field_description:stock_quant_package_move_wizard.field_stock_quant_move_wizard_line__quant_quantity
msgid "On Hand Qty"
msgstr ""
#. module: stock_quant_package_move_wizard
#: model:ir.model.fields,field_description:stock_quant_package_move_wizard.field_stock_quant_move_wizard__picking_type_id
msgid "Picking Type"
msgstr ""
#. module: stock_quant_package_move_wizard
#: model:ir.model.fields,field_description:stock_quant_package_move_wizard.field_stock_quant_move_wizard_line__product_id
msgid "Product"
msgstr ""
#. module: stock_quant_package_move_wizard
#: model:ir.model.fields,field_description:stock_quant_package_move_wizard.field_stock_quant_move_wizard_line__quantity
msgid "Qty to Move"
msgstr ""
#. module: stock_quant_package_move_wizard
#: model:ir.model.fields,field_description:stock_quant_package_move_wizard.field_stock_quant_move_wizard_line__quant_id
msgid "Quant"
msgstr ""
#. module: stock_quant_package_move_wizard
#: model:ir.model.fields,field_description:stock_quant_package_move_wizard.field_stock_quant_move_wizard_line__wizard_id
msgid "Quant Move"
msgstr ""
#. module: stock_quant_package_move_wizard
#: model:ir.model.fields,help:stock_quant_package_move_wizard.field_stock_quant_move_wizard_line__quant_quantity
msgid ""
"Quantity of products in this quant, in the default unit of measure of the "
"product"
msgstr ""
#. module: stock_quant_package_move_wizard
#: model:ir.model,name:stock_quant_package_move_wizard.model_stock_quant
msgid "Quants"
msgstr ""
#. module: stock_quant_package_move_wizard
#: model:ir.model.fields,field_description:stock_quant_package_move_wizard.field_stock_quant_move_wizard__origin
msgid "Source Document"
msgstr ""
#. module: stock_quant_package_move_wizard
#: model_terms:ir.ui.view,arch_db:stock_quant_package_move_wizard.stock_quant_move_wizard_form
msgid "Unit"
msgstr ""
#. module: stock_quant_package_move_wizard
#: model:ir.model.fields,field_description:stock_quant_package_move_wizard.field_stock_quant_move_wizard_line__uom_id
msgid "Unit of Measure"
msgstr ""
#. module: stock_quant_package_move_wizard
#: model:ir.model,name:stock_quant_package_move_wizard.model_stock_quant_move_wizard
msgid "Wizard to Move Quants"
msgstr ""
#. module: stock_quant_package_move_wizard
#. odoo-python
#: code:addons/stock_quant_package_move_wizard/models/stock_quant.py:0
#, python-format
msgid ""
"You are trying to move %(qty)s %(uom)s of a quant of product "
"%(product_name)s that has a quantity of %(quant_quantity)s %(uom)s."
msgstr ""
#. module: stock_quant_package_move_wizard
#. odoo-python
#: code:addons/stock_quant_package_move_wizard/models/stock_quant.py:0
#, python-format
msgid ""
"You are trying to move %(qty)s %(uom)s of a quant of product "
"%(product_name)s to %(dest_location)s, but it is already on that location!"
msgstr ""
#. module: stock_quant_package_move_wizard
#. odoo-python
#: code:addons/stock_quant_package_move_wizard/models/stock_quant.py:0
#, python-format
msgid ""
"You are trying to move %(qty)s %(uom)s of a quant of product "
"%(product_name)s, but that quant has a negative quantity (%(quant_quantity)s"
" %(uom)s)."
msgstr ""
#. module: stock_quant_package_move_wizard
#. odoo-python
#: code:addons/stock_quant_package_move_wizard/models/stock_quant.py:0
#, python-format
msgid ""
"You are trying to move %(qty)s %(uom)s of a quant of product "
"%(product_name)s: the quantity to move must be strictly positive."
msgstr ""

View File

@@ -0,0 +1 @@
from . import stock_quant

View File

@@ -0,0 +1,127 @@
# Copyright 2022 Akretion France (http://www.akretion.com/)
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html
from odoo import _, models
from odoo.exceptions import UserError
from odoo.tools import float_compare
from odoo.tools.misc import formatLang
class StockQuant(models.Model):
_inherit = "stock.quant"
def _prepare_move_to_stock_move(self, qty, dest_location, picking_id, origin=False):
# qty is in the product's uom_id
self.ensure_one()
if dest_location.usage == "view":
raise UserError(
_("Cannot move to '%s' which is a view location.")
% dest_location.display_name
)
prec = self.env["decimal.precision"].precision_get("Product Unit of Measure")
raise_dict = {
"qty": formatLang(self.env, qty, digits=prec),
"uom": self.product_id.uom_id.display_name,
"product_name": self.product_id.display_name,
"dest_location": dest_location.display_name,
"quant_quantity": formatLang(self.env, self.quantity, digits=prec),
}
if self.location_id == dest_location:
raise UserError(
_(
"You are trying to move %(qty)s %(uom)s of a quant of product "
"%(product_name)s to %(dest_location)s, but it is already on "
"that location!"
)
% raise_dict
)
if float_compare(qty, self.quantity, precision_digits=prec) > 0:
raise UserError(
_(
"You are trying to move %(qty)s %(uom)s of a quant of product "
"%(product_name)s that has a quantity of "
"%(quant_quantity)s %(uom)s."
)
% raise_dict
)
if float_compare(self.quantity, 0, precision_digits=prec) <= 0:
raise UserError(
_(
"You are trying to move %(qty)s %(uom)s of a quant of product "
"%(product_name)s, but that quant has a negative quantity "
"(%(quant_quantity)s %(uom)s)."
)
% raise_dict
)
if float_compare(qty, 0, precision_digits=prec) <= 0:
raise UserError(
_(
"You are trying to move %(qty)s %(uom)s of a quant of product "
"%(product_name)s: the quantity to move must be strictly positive."
)
% raise_dict
)
product_id = self.product_id.id
location_id = self.location_id.id
location_dest_id = dest_location.id
uom_id = self.product_id.uom_id.id
vals = {
"picking_id": picking_id,
"name": "%s: Move to %s"
% (self.product_id.display_name, dest_location.display_name),
"product_id": product_id,
"location_id": location_id,
"location_dest_id": location_dest_id,
"product_uom_qty": qty,
"product_uom": uom_id,
"origin": origin,
"move_line_ids": [
(
0,
0,
{
"product_id": product_id,
"product_uom_id": uom_id,
"qty_done": qty,
"location_id": location_id,
"location_dest_id": location_dest_id,
"lot_id": self.lot_id.id or False,
},
)
],
}
return vals
def _prepare_move_to_stock_picking(self, dest_location, picking_type, origin=False):
vals = {
"picking_type_id": picking_type.id,
"location_dest_id": dest_location.id,
"location_id": picking_type.default_location_src_id.id,
"origin": origin,
}
return vals
def move_full_quant_to(self, dest_location, picking_type=False, origin=False):
assert dest_location
picking_id = False
if picking_type:
picking_vals = self._prepare_move_to_stock_picking(
dest_location, picking_type, origin=origin
)
picking_id = self.env["stock.picking"].create(picking_vals).id
smo = self.env["stock.move"]
stock_move_ids = []
for quant in self:
vals = quant._prepare_move_to_stock_move(
quant.quantity, dest_location, picking_id, origin=origin
)
new_move = smo.create(vals)
# No group has write access on stock.quant -> we need sudo()
new_move._action_done()
assert new_move.state == "done"
stock_move_ids.append(new_move.id)
return {
"picking_id": picking_id,
"stock_move_ids": stock_move_ids,
}

View File

@@ -0,0 +1,3 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_stock_quant_move_wizard,Full access on stock.quant.move.wizard,model_stock_quant_move_wizard,stock.group_stock_user,1,1,1,1
access_stock_quant_move_wizard_line,Full access on stock.quant.move.wizard.line,model_stock_quant_move_wizard_line,stock.group_stock_user,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_stock_quant_move_wizard Full access on stock.quant.move.wizard model_stock_quant_move_wizard stock.group_stock_user 1 1 1 1
3 access_stock_quant_move_wizard_line Full access on stock.quant.move.wizard.line model_stock_quant_move_wizard_line stock.group_stock_user 1 1 1 1

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8" ?>
<!--
Copyright 2022 Akretion France (http://www.akretion.com/)
@author: Alexis de Lattre <alexis.delattre@akretion.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->
<odoo>
<!-- Maybe it's not a good idea to put a button on tree view of quants
from product form view, because all the direct-access buttons on this tree
view are for inventory, so it may be hard for users to understand that
this button has nothing to do with inventory
<record id="view_stock_quant_tree_inventory_editable" model="ir.ui.view">
<field name="model">stock.quant</field>
<field name="inherit_id" ref="stock.view_stock_quant_tree_inventory_editable" />
<field name="arch" type="xml">
<button name="stock.action_stock_inventory_adjustement_name" position="before">
<button
type="action"
name="%(stock_quant_move_wizard_action)d"
string="Move"
/>
</button>
</field>
</record>
-->
</odoo>

View File

@@ -0,0 +1 @@
from . import stock_quant_move_wizard

View File

@@ -0,0 +1,114 @@
# Copyright 2022 Akretion France (http://www.akretion.com/)
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html
from odoo import api, fields, models
class StockQuantMoveWizard(models.TransientModel):
_name = "stock.quant.move.wizard"
_description = "Wizard to Move Quants"
_check_company_auto = True
line_ids = fields.One2many(
"stock.quant.move.wizard.line", "wizard_id", string="Lines"
)
company_id = fields.Many2one("res.company", required=True)
location_dest_id = fields.Many2one(
"stock.location",
string="Destination Location",
domain="[('usage', '!=', 'view'), ('company_id', '=', company_id)]",
check_company=True,
required=True,
)
picking_type_id = fields.Many2one(
"stock.picking.type",
domain="[('company_id', '=', company_id)]",
check_company=True,
)
origin = fields.Char(string="Source Document")
# Idea : add a bool option 'move even if reserved' (= current behavior)
@api.onchange("picking_type_id")
def picking_type_id_change(self):
if self.picking_type_id and self.picking_type_id.default_location_dest_id:
self.location_dest_id = self.picking_type_id.default_location_dest_id
@api.model
def default_get(self, fields_list):
res = super().default_get(fields_list)
assert self._context.get("active_model") == "stock.quant"
company_id = self.env.company.id
quants_ids = self._context.get("active_ids", [])
quants = self.env["stock.quant"].browse(quants_ids)
lines = []
for quant in quants.filtered(
lambda q: not q.package_id and q.company_id.id == company_id
):
lines.append((0, 0, {"quant_id": quant.id, "quantity": quant.quantity}))
picking_type = self.env["stock.picking.type"].search(
[("code", "=", "internal"), ("company_id", "=", company_id)], limit=1
)
res.update(
{
"line_ids": lines,
"company_id": company_id,
"picking_type_id": picking_type and picking_type.id or False,
}
)
return res
def run(self):
self.ensure_one()
picking_id = False
if self.picking_type_id:
picking_vals = self.env["stock.quant"]._prepare_move_to_stock_picking(
self.location_dest_id, self.picking_type_id, origin=self.origin
)
picking_id = self.env["stock.picking"].create(picking_vals).id
smo = self.env["stock.move"]
for line in self.line_ids:
quant = line.quant_id
assert not quant.package_id
vals = quant._prepare_move_to_stock_move(
line.quantity, self.location_dest_id, picking_id, origin=self.origin
)
new_move = smo.create(vals)
new_move._action_done()
assert new_move.state == "done"
action = {}
if picking_id and self._context.get("run_show_picking"):
action = self.env["ir.actions.actions"]._for_xml_id(
"stock.stock_picking_action_picking_type"
)
action.update(
{
"res_id": picking_id,
"view_mode": "form,tree,pivot",
"views": False,
"view_id": False,
}
)
return action
class StockQuantMoveWizardLine(models.TransientModel):
_name = "stock.quant.move.wizard.line"
_description = "Lines of the wizard to move quants"
wizard_id = fields.Many2one(
comodel_name="stock.quant.move.wizard", string="Quant Move", ondelete="cascade"
)
quant_id = fields.Many2one(
comodel_name="stock.quant",
string="Quant",
required=True,
)
product_id = fields.Many2one(related="quant_id.product_id")
quant_quantity = fields.Float(related="quant_id.quantity", string="On Hand Qty")
quantity = fields.Float(string="Qty to Move", digits="Product Unit of Measure")
uom_id = fields.Many2one(related="quant_id.product_id.uom_id")
lot_id = fields.Many2one(related="quant_id.lot_id")
src_location_id = fields.Many2one(
related="quant_id.location_id", string="Current Location"
)

View File

@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="utf-8" ?>
<!--
Copyright 2022 Akretion France (http://www.akretion.com/)
@author: Alexis de Lattre <alexis.delattre@akretion.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->
<odoo>
<record id="stock_quant_move_wizard_form" model="ir.ui.view">
<field name="model">stock.quant.move.wizard</field>
<field name="arch" type="xml">
<form>
<group name="main">
<field name="company_id" invisible="1" />
<field
name="picking_type_id"
options="{'no_open': True, 'no_create': True}"
/>
<field
name="location_dest_id"
options="{'no_open': True, 'no_create': True}"
/>
<field name="origin" />
<field name="line_ids" nolabel="1" colspan="2">
<tree editable="bottom" create="0">
<field name="quant_id" invisible="1" />
<field name="src_location_id" />
<field name="product_id" />
<field name="lot_id" groups="stock.group_production_lot" />
<field name="quant_quantity" sum="1" />
<field name="quantity" sum="1" />
<field name="uom_id" groups="uom.group_uom" string="Unit" />
</tree>
</field>
</group>
<footer>
<button name="run" string="Move" type="object" class="btn-primary" />
<button string="Cancel" special="cancel" />
</footer>
</form>
</field>
</record>
<record id="stock_quant_move_wizard_action" model="ir.actions.act_window">
<field name="name">Move to Another Location</field>
<field name="res_model">stock.quant.move.wizard</field>
<field name="view_mode">form</field>
<field name="target">new</field>
<field name="binding_model_id" ref="stock.model_stock_quant" />
<field name="binding_view_types">list</field>
</record>
</odoo>