Compare commits
2 Commits
14-fix-mig
...
14.0-_comp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
947d287c13 | ||
|
|
a7ee151f15 |
@@ -1,27 +0,0 @@
|
|||||||
===============================
|
|
||||||
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>
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
from . import report
|
|
||||||
from . import wizard
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
# 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,
|
|
||||||
}
|
|
||||||
@@ -1,213 +0,0 @@
|
|||||||
# 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 :"
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
from . import bank_reconciliation_xlsx
|
|
||||||
@@ -1,296 +0,0 @@
|
|||||||
# 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
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
<?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>
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
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,23 +0,0 @@
|
|||||||
<?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>
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
<?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>
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
from . import bank_reconciliation_report_wizard
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
# 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
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
<?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>
|
|
||||||
@@ -29,6 +29,7 @@
|
|||||||
'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',
|
||||||
|
|||||||
@@ -6,4 +6,4 @@ from odoo import SUPERUSER_ID, api
|
|||||||
|
|
||||||
def post_init_hook(cr, registry):
|
def post_init_hook(cr, registry):
|
||||||
env = api.Environment(cr, SUPERUSER_ID, {})
|
env = api.Environment(cr, SUPERUSER_ID, {})
|
||||||
env["account.move.line"].update_matching_number()
|
# env["account.move.line"].update_matching_number()
|
||||||
|
|||||||
@@ -6,4 +6,4 @@ from odoo import SUPERUSER_ID, api
|
|||||||
|
|
||||||
def migrate(cr, version):
|
def migrate(cr, version):
|
||||||
env = api.Environment(cr, SUPERUSER_ID, {})
|
env = api.Environment(cr, SUPERUSER_ID, {})
|
||||||
env["account.move.line"].update_matching_number()
|
# env["account.move.line"].update_matching_number()
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
import logging
|
import logging
|
||||||
|
from psycopg2 import IntegrityError
|
||||||
|
|
||||||
from odoo import api, fields, models, _
|
from odoo import api, fields, models, _
|
||||||
from odoo.exceptions import UserError
|
from odoo.exceptions import UserError
|
||||||
@@ -112,16 +113,6 @@ class AccountMove(models.Model):
|
|||||||
# self.invalidate_cache()
|
# self.invalidate_cache()
|
||||||
# return res
|
# return res
|
||||||
|
|
||||||
def _reverse_moves(self, default_values_list=None, cancel=False):
|
|
||||||
reverse_moves = super()._reverse_moves(
|
|
||||||
default_values_list=default_values_list, cancel=cancel)
|
|
||||||
# In the simple scenario 1 invoice -> 1 refund, we add a message in the chatter
|
|
||||||
# of the invoice and in the chatter of the refund
|
|
||||||
if len(self) == 1 and len(reverse_moves) == 1:
|
|
||||||
self.message_post(body=_("A reverse journal entry <a href=# data-oe-model=account.move data-oe-id=%d>%s</a> has been generated.") % (reverse_moves.id, reverse_moves.display_name))
|
|
||||||
reverse_moves.message_post(body=_("This journal entry has been generated as the reverse of <a href=# data-oe-model=account.move data-oe-id=%d>%s</a>.") % (self.id, self.display_name))
|
|
||||||
return reverse_moves
|
|
||||||
|
|
||||||
def delete_lines_qty_zero(self):
|
def delete_lines_qty_zero(self):
|
||||||
lines = self.env['account.move.line'].search([
|
lines = self.env['account.move.line'].search([
|
||||||
('display_type', '=', False),
|
('display_type', '=', False),
|
||||||
@@ -270,23 +261,25 @@ class AccountMoveLine(models.Model):
|
|||||||
def update_matching_number(self):
|
def update_matching_number(self):
|
||||||
records = self.search([("matching_number", "=", "P")])
|
records = self.search([("matching_number", "=", "P")])
|
||||||
_logger.info(f"Update partial reconcile number for {len(records)} lines")
|
_logger.info(f"Update partial reconcile number for {len(records)} lines")
|
||||||
records.compute_partial_matching_number()
|
records._compute_matching_number()
|
||||||
|
|
||||||
def compute_partial_matching_number(self):
|
def _compute_matching_number(self):
|
||||||
# TODO maybe it will be better to have the same maching_number for
|
# TODO maybe it will be better to have the same maching_number for
|
||||||
# all partial so it will be easier to group by
|
# all partial so it will be easier to group by
|
||||||
for record in self:
|
for record in self:
|
||||||
if record.matching_number == "P":
|
try:
|
||||||
matching_number = ", ".join([
|
super(AccountMoveLine, record)._compute_matching_number()
|
||||||
"a%d" % pr.id
|
if record.matching_number == "P":
|
||||||
for pr in record.matched_debit_ids + record.matched_credit_ids
|
record.matching_number = ", ".join([
|
||||||
])
|
"a%d" % pr.id
|
||||||
# use sql to avoid triggering python checks
|
for pr in record.matched_debit_ids + record.matched_credit_ids
|
||||||
self.env.cr.execute(
|
])
|
||||||
"""
|
except IntegrityError as error:
|
||||||
UPDATE account_move_line SET matching_number = %s WHERE id = %s
|
_logger.info(
|
||||||
""", (matching_number, record.id)
|
f"unable to update matching number for line ID {record.id},"
|
||||||
|
"{record.display_name}"
|
||||||
)
|
)
|
||||||
|
_logger.info(error)
|
||||||
|
|
||||||
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
|
||||||
|
|||||||
@@ -13,9 +13,6 @@
|
|||||||
<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"
|
||||||
|
|||||||
23
account_usability/views/res_partner.xml
Normal file
23
account_usability/views/res_partner.xml
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<?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>
|
||||||
@@ -12,14 +12,13 @@ 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 and moves.move_type not in ('out_invoice', 'in_invoice'):
|
if len(moves) == 1:
|
||||||
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:
|
||||||
|
|||||||
@@ -19,10 +19,4 @@
|
|||||||
</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>
|
||||||
|
|||||||
@@ -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": "14.0.0.0.0",
|
'version': '12.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,13 +21,11 @@ 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": "https://www.akretion.com",
|
'website': 'http://www.akretion.com',
|
||||||
"depends": [
|
'depends': ['mail'],
|
||||||
"mail",
|
'data': [
|
||||||
|
'menu_view.xml'
|
||||||
],
|
],
|
||||||
"data": [
|
'installable': False,
|
||||||
"menu_view.xml",
|
|
||||||
],
|
|
||||||
"installable": True,
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<odoo>
|
<odoo>
|
||||||
|
|
||||||
<menuitem id="conf_tech" parent="base.menu_administration" name="🧰" groups="base.group_erp_manager" sequence="1"/>
|
<menuitem id="conf_tech" parent="base.menu_administration" name="🧰" groups="base.group_erp_manager" sequence="100"/>
|
||||||
<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" />
|
||||||
|
|||||||
@@ -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_activity.xml',
|
#'views/mail_view.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',
|
||||||
|
|||||||
@@ -1,3 +1,2 @@
|
|||||||
from . import res_partner
|
from . import res_partner
|
||||||
from . import mail_activity
|
|
||||||
from . import mail_template
|
from . import mail_template
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
# 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
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
<?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>
|
|
||||||
@@ -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', 'categ_id')
|
@api.constrains('taxes_id', 'supplier_taxes_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'
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
from . import models
|
|
||||||
from .post_install import set_product_detailed_type
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
# 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,
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
from . import product_template
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
# 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)
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
# 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')
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
<?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>
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
from . import models
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
# 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,
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
from . import product_template
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
# 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'})
|
|
||||||
@@ -41,7 +41,6 @@ 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',
|
||||||
|
|||||||
@@ -1,14 +1,11 @@
|
|||||||
# Copyright 2020-2023 Akretion France (http://www.akretion.com/)
|
# -*- coding: utf-8 -*-
|
||||||
|
# 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, is_valid
|
from stdnum.ean import calc_check_digit
|
||||||
from barcode import EAN13, EAN8
|
|
||||||
from barcode.writer import ImageWriter, SVGWriter
|
|
||||||
import base64
|
|
||||||
import io
|
|
||||||
|
|
||||||
|
|
||||||
class ProductTemplate(models.Model):
|
class ProductTemplate(models.Model):
|
||||||
@@ -37,8 +34,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["ir.actions.actions"]._for_xml_id(
|
action = self.env.ref(
|
||||||
'product_print_zpl_barcode.product_print_zpl_barcode_action')
|
'product_print_zpl_barcode.product_print_zpl_barcode_action').sudo().read()[0]
|
||||||
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',
|
||||||
@@ -49,50 +46,6 @@ 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:
|
||||||
|
|||||||
@@ -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, calc_check_digit
|
from stdnum.ean import is_valid
|
||||||
import base64
|
import base64
|
||||||
import re
|
import re
|
||||||
|
|
||||||
@@ -55,7 +55,8 @@ 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(related='product_id.uom_id')
|
uom_id = fields.Many2one(
|
||||||
|
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(
|
||||||
@@ -63,13 +64,15 @@ 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(related='rule_id.type', string="Barcode Type")
|
barcode_type = fields.Selection(
|
||||||
|
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')
|
], required=True, default='38x25', string='Label Size')
|
||||||
pricelist_id = fields.Many2one(
|
pricelist_id = fields.Many2one(
|
||||||
'product.pricelist', string='Pricelist', required=True)
|
'product.pricelist', string='Pricelist', required=True)
|
||||||
currency_id = fields.Many2one(related='pricelist_id.currency_id')
|
currency_id = fields.Many2one(
|
||||||
|
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(
|
||||||
@@ -137,14 +140,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(_(
|
||||||
@@ -163,15 +166,11 @@ 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':
|
||||||
# I don't call bno.sanitize_ean() due to this bug:
|
barcode = bno.sanitize_ean(barcode)
|
||||||
# https://github.com/odoo/odoo/pull/114112
|
# print "barcode FINAL=", barcode
|
||||||
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',
|
||||||
@@ -271,8 +270,7 @@ 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["ir.actions.actions"]._for_xml_id(
|
action = self.env.ref('product_print_zpl_barcode.product_print_zpl_barcode_action').sudo().read()[0]
|
||||||
'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,
|
||||||
@@ -287,8 +285,7 @@ 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["ir.actions.actions"]._for_xml_id(
|
action = self.env.ref('product_print_zpl_barcode.product_print_zpl_barcode_action').sudo().read()[0]
|
||||||
'product_print_zpl_barcode.product_print_zpl_barcode_action')
|
|
||||||
action.update({
|
action.update({
|
||||||
'views': False,
|
'views': False,
|
||||||
'context': self._context,
|
'context': self._context,
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
from . import models
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
# 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,
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
from . import product
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
# 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'
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
<?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>
|
|
||||||
@@ -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)], ondelete='restrict')
|
name = fields.Many2one(domain=[('parent_id', '=', False)])
|
||||||
|
|||||||
@@ -26,19 +26,18 @@
|
|||||||
<field name="model">purchase.report</field>
|
<field name="model">purchase.report</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<tree>
|
<tree>
|
||||||
<field name="order_id" optional="show"/>
|
|
||||||
<field name="commercial_partner_id"/>
|
<field name="commercial_partner_id"/>
|
||||||
<field name="date_order" optional="show"/>
|
<field name="date_order"/>
|
||||||
<field name="date_approve" optional="show"/>
|
<field name="date_approve"/>
|
||||||
<field name="product_id"/>
|
<field name="product_id"/>
|
||||||
<field name="qty_ordered" sum="1"/>
|
<field name="qty_ordered" sum="1"/>
|
||||||
<field name="qty_received" sum="1"/>
|
<field name="qty_received" sum="1"/>
|
||||||
<field name="qty_billed" sum="1"/>
|
<field name="qty_billed" sum="1"/>
|
||||||
<field name="product_uom" groups="uom.group_uom"/>
|
<field name="product_uom"/>
|
||||||
<field name="price_total" sum="1"/>
|
<field name="price_total" sum="1"/>
|
||||||
<field name="account_analytic_id" groups="analytic.group_analytic_accounting" optional="show"/>
|
<field name="account_analytic_id" groups="analytic.group_analytic_accounting"/>
|
||||||
<field name="currency_id" invisible="1"/>
|
<field name="currency_id" invisible="1"/>
|
||||||
<field name="user_id" optional="hide"/>
|
<field name="user_id"/>
|
||||||
<field name="state"/>
|
<field name="state"/>
|
||||||
</tree>
|
</tree>
|
||||||
</field>
|
</field>
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
from . import models
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
# 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,
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
from . import crm_lead
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
# 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
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
../../../../account_usability
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
import setuptools
|
|
||||||
|
|
||||||
setuptools.setup(
|
|
||||||
setup_requires=['setuptools-odoo'],
|
|
||||||
odoo_addon=True,
|
|
||||||
)
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
../../../../base_usability
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
import setuptools
|
|
||||||
|
|
||||||
setuptools.setup(
|
|
||||||
setup_requires=['setuptools-odoo'],
|
|
||||||
odoo_addon=True,
|
|
||||||
)
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
from . import models
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
# 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': 'Stock Move Line Auto-fill All',
|
|
||||||
'version': '14.0.1.0.0',
|
|
||||||
'category': 'Warehouse',
|
|
||||||
'license': 'AGPL-3',
|
|
||||||
'summary': 'Add button on picking to auto-fill done qty',
|
|
||||||
'description': """
|
|
||||||
This module is an alternative to the OCA module **stock_move_line_auto_fill** from https://github.com/OCA/stock-logistics-workflow/
|
|
||||||
The OCA module doesn't auto-fill the stock move lines with lots. This module does.
|
|
||||||
|
|
||||||
This module has been written by Alexis de Lattre from Akretion
|
|
||||||
<alexis.delattre@akretion.com>.
|
|
||||||
""",
|
|
||||||
'author': 'Akretion',
|
|
||||||
'maintainers': ['alexis-via'],
|
|
||||||
"development_status": "Mature",
|
|
||||||
'website': 'https://github.com/akretion/odoo-usability',
|
|
||||||
'depends': ['stock'],
|
|
||||||
'data': [
|
|
||||||
'views/stock_picking.xml',
|
|
||||||
],
|
|
||||||
'installable': True,
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
from . import stock_picking
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
# 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, _
|
|
||||||
from odoo.tools import float_compare, float_is_zero
|
|
||||||
from odoo.exceptions import UserError
|
|
||||||
|
|
||||||
|
|
||||||
class StockPicking(models.Model):
|
|
||||||
_inherit = 'stock.picking'
|
|
||||||
|
|
||||||
autofill_done = fields.Boolean(readonly=True)
|
|
||||||
|
|
||||||
def button_stock_move_line_autofill(self):
|
|
||||||
self.ensure_one()
|
|
||||||
prec = self.env['decimal.precision'].precision_get(
|
|
||||||
'Product Unit of Measure')
|
|
||||||
for ml in self.move_line_ids_without_package:
|
|
||||||
if ml.product_id and float_compare(ml.product_uom_qty, 0, precision_digits=prec) > 0 and float_is_zero(ml.qty_done, precision_digits=prec):
|
|
||||||
if (
|
|
||||||
ml.product_id.tracking in ('lot', 'serial') and
|
|
||||||
not ml.lot_id and
|
|
||||||
not ml.lot_name):
|
|
||||||
raise UserError(_(
|
|
||||||
"Autofill is not possible: the lot is not set "
|
|
||||||
"on move line with product '%s' quantity %s %s.")
|
|
||||||
% (
|
|
||||||
ml.product_id.display_name,
|
|
||||||
ml.product_uom_qty,
|
|
||||||
ml.product_uom_id.display_name
|
|
||||||
))
|
|
||||||
ml.write({'qty_done': ml.product_uom_qty})
|
|
||||||
self.write({'autofill_done': True})
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!--
|
|
||||||
Copyright 2023 Akretion France (http://www.akretion.com/)
|
|
||||||
@author: Alexis de Lattre <alexis.delattre@akretion.com>
|
|
||||||
-->
|
|
||||||
|
|
||||||
<odoo>
|
|
||||||
|
|
||||||
<record id="view_picking_form" model="ir.ui.view">
|
|
||||||
<field name="model">stock.picking</field>
|
|
||||||
<field name="inherit_id" ref="stock.view_picking_form"/>
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<button name="button_validate" type="object" class="oe_highlight" position="before">
|
|
||||||
<button name="button_stock_move_line_autofill" type="object" string="Auto-Fill" attrs="{'invisible': ['|', ('state', '!=', 'assigned'), ('autofill_done', '=', True)]}" groups="stock.group_stock_user" help="This button will copy the 'Reserved' qty on the 'Done' qty for all the operations of this picking with a null 'Done' qty."/>
|
|
||||||
</button>
|
|
||||||
<field name="origin" position="after">
|
|
||||||
<field name="autofill_done" invisible="1"/>
|
|
||||||
</field>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
</odoo>
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
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>
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
from . import models
|
|
||||||
from . import wizards
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
# 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,
|
|
||||||
}
|
|
||||||
@@ -1,223 +0,0 @@
|
|||||||
# 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."
|
|
||||||
@@ -1,214 +0,0 @@
|
|||||||
# 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 ""
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
from . import stock_quant
|
|
||||||
@@ -1,127 +0,0 @@
|
|||||||
# 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,
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
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,28 +0,0 @@
|
|||||||
<?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>
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
from . import stock_quant_move_wizard
|
|
||||||
@@ -1,114 +0,0 @@
|
|||||||
# 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"
|
|
||||||
)
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
<?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>
|
|
||||||
Reference in New Issue
Block a user