[MERGE] forward port branch saas-12.1 up to 6878c9f342

This commit is contained in:
Christophe Simonis
2019-03-21 13:58:37 +01:00
84 changed files with 2499 additions and 1691 deletions
+12 -5
View File
@@ -30,7 +30,7 @@
# Ana Juaristi <ajuaristio@gmail.com>, 2018
# Manuel Vázquez Acosta <mva.led@gmail.com>, 2018
# Javier Ramirez <elbomba887@gmail.com>, 2018
# e2f_EPV Translator <e2f_EPV@outlook.com>, 2018
# 966ff43e6966712895a590e7320ca288, 2018
# Christopher Ormaza <chris.ormaza@gmail.com>, 2018
# Alejandro Die Sanchis <marketing@domatix.com>, 2018
# Angel Moya - PESOL <angel.moya@pesol.es>, 2018
@@ -40,7 +40,7 @@
# Cris Martin <cristina.marrod@gmail.com>, 2018
# Mateo Tibaquirá Palacios <nestormateo@gmail.com>, 2019
# Carlos Lopez <celm1990@hotmail.com>, 2019
# e2f_es_ t7 <e2f_es_t7@outlook.com>, 2019
# 36700cd705c35e1852bdffcd0b296648, 2019
# Pilar Marín <pilar@odooperu.pe>, 2019
# Cristian Salamea <ovnicraft@gmail.com>, 2019
# Pedro M. Baeza <pedro.baeza@gmail.com>, 2019
@@ -73,6 +73,7 @@
# Luis Marin <marin.guadarrama@gmail.com>, 2019
# Edilianny Sánchez <esanchez@vauxoo.com>, 2019
# Gabriel Umana <gabriel.umana@delfixcr.com>, 2019
# Jesse Garza <jga@odoo.com>, 2019
#
msgid ""
msgstr ""
@@ -2305,7 +2306,7 @@ msgstr "Preconfiguración del asiento de conciliación bancaria"
#: model:ir.model.fields,field_description:account.field_res_company__account_bank_reconciliation_start
#: model:ir.model.fields,field_description:account.field_res_config_settings__account_bank_reconciliation_start
msgid "Bank Reconciliation Threshold"
msgstr ""
msgstr "Fecha de corte para conciliaciones"
#. module: account
#: model:ir.model,name:account.model_account_bank_statement
@@ -2328,7 +2329,7 @@ msgstr "Caja Bancaria"
#. module: account
#: model:ir.model,name:account.model_account_bank_statement_closebalance
msgid "Bank Statement Closing Balance"
msgstr ""
msgstr "Saldo final del extracto bancario"
#. module: account
#: model:ir.model,name:account.model_account_bank_statement_line
@@ -2356,6 +2357,7 @@ msgstr "Cuenta bancaria usada en esta transacción"
msgid ""
"Bank account type: Normal or IBAN. Inferred from the bank account number."
msgstr ""
"Tipo de cuenta bancaria: Normal o IBAN. Se infiere del número de cuenta."
#. module: account
#: model:account.account.type,name:account.data_account_type_liquidity
@@ -2365,7 +2367,7 @@ msgstr "Banco y caja"
#. module: account
#: model:ir.model,name:account.model_account_setup_bank_manual_config
msgid "Bank setup manual config"
msgstr ""
msgstr "Configuración manual de datos bancarios"
#. module: account
#: model:ir.model.fields,field_description:account.field_account_move_line__statement_line_id
@@ -2832,6 +2834,9 @@ msgid ""
"Changing your company name is not allowed once invoices have been issued for"
" your account. Please contact us directly for this operation."
msgstr ""
"Cambiar el nombre de la compañía no está permitido una vez que se han "
"emitido facturas para su cuenta. Por favor contáctenos para realizar esta "
"operación."
#. module: account
#: code:addons/account/controllers/portal.py:106
@@ -2989,6 +2994,8 @@ msgid ""
"Choose how you want to credit this invoice. You cannot Modify and Cancel if "
"the invoice is already reconciled"
msgstr ""
"Elija cómo quiere acreditar esta factura. No puede modificarla o cancelarla "
"si la factura ya fue conciliada"
#. module: account
#: model_terms:ir.actions.act_window,help:account.actions_account_fiscal_year
+827 -409
View File
File diff suppressed because it is too large Load Diff
+21 -20
View File
@@ -45,7 +45,7 @@ msgid ""
" * The 'Cancelled' status is used when user cancel invoice."
msgstr ""
" * De 'Concept' status wordt gebruikt wanneer een gebruiker een nieuwe factuur aan het ingeven is. Deze factuur werd nog niet bevestigd.\n"
" * De 'Open' status wordt gebruikt wanneer de gebruiker een factuur aanmaakt, een nieuwe factuurnummer wordt gegenereerd. De factuur blijft open tot deze betaald is.\n"
" * De 'Open' status wordt gebruikt wanneer de gebruiker een factuur aanmaakt, er wordt een nieuwe factuurnummer gegenereerd. De factuur blijft open tot deze betaald is.\n"
" * De 'In betaling' status wordt gebruikt wanneer betalingen werden ontvangen, voor de gehele factuur of een gedeelte ervan, in een dagboek dat enkel werd ingesteld voor bank transacties en sommige nog niet werden afgeletterd.\n"
" * De 'Betaald' status wordt automatisch ingesteld wanneer de factuur betaald is. De gerelateerde boekingen kunnen zowel reeds afgeletterd zijn als niet afgeletterd zijn.\n"
" * De 'Geannuleerd' status wordt gebruikt wanneer de gebruiker de factuur annuleert."
@@ -171,7 +171,7 @@ msgstr ""
" Beste${object.partner_id.name}<br/><br/>\n"
" Bedankt voor uw betaling\n"
" Hierbij sturen wij u het afschrift van de betaling <strong>${(object.name or '').replace('/','-')}</strong> voor het bedrag van\n"
" tot <strong>${format_amount(object.amount, object.currency_id)}</strong> van ${object.company_id.name}.\n"
" <strong>${format_amount(object.amount, object.currency_id)}</strong> van ${object.company_id.name}.\n"
" <br/><br/>\n"
" Twijfel zeker niet om ons te contacteren indien u nog verdere vragen heeft.\n"
" <br/><br/>\n"
@@ -228,7 +228,7 @@ msgstr ""
" factuur\n"
" %endif\n"
" % if object.origin:\n"
" (with reference: ${object.origin})\n"
" (met referentie: ${object.origin})\n"
" % endif\n"
" voor een bedrag van <strong>${format_amount(object.amount_total, object.currency_id)}</strong>\n"
" ten aanzien van ${object.company_id.name}.\n"
@@ -1411,8 +1411,8 @@ msgid ""
msgstr ""
"Voeg een EPC QR codering toe aan uw facturen. Dit stelt uw klanten in staat "
"om onmiddellijk over te gaan tot betaling via hun mobiele bank applicaties. "
"EPC QR codering worden gebruikt door de meerderheid van Europese banken voor"
" de verwerking van de SEPA verrichtingen."
"EPC QR coderingen worden gebruikt door de meerderheid van Europese banken "
"voor de verwerking van de SEPA verrichtingen."
#. module: account
#: model_terms:ir.ui.view,arch_db:account.view_move_form
@@ -5277,8 +5277,9 @@ msgid ""
" type and recipient bank account in the generated payments. If disabled,\n"
" a distinct payment will be generated for each invoice."
msgstr ""
"Indien geactiveerd, groepeert facturen volgens commerciële relatie, factuurrekening, aard en herkomst bankrekening in de verwerkte betalingen. \n"
"Indien gedeactiveerd, genereert een aparte betaling per factuur. "
"Indien geactiveerd, groepeert dit facturen volgens commerciële relatie, factuurrekening, aard en herkomst van de bankrekening in de verwerkte betalingen. \n"
"Indien gedeactiveerd, \n"
"genereert het een aparte betaling per factuur. "
#. module: account
#: model:ir.model.fields,help:account.field_account_tax__include_base_amount
@@ -6324,7 +6325,7 @@ msgid ""
"expense account on the product category will be used."
msgstr ""
"Laat dit veld leeg om de standaard waarde uit de productcategorie te "
"gebruiken. Bij anlgo-saksische boekhouding met realtime waardering, zal de "
"gebruiken. Bij anglo-saksische boekhouding met realtime waardering, zal de "
"kostenrekening op de productcategorie gebruikt worden."
#. module: account
@@ -6781,11 +6782,11 @@ msgid ""
"Batch Deposit: Encase several customer checks at once by generating a batch deposit to submit to your bank. When encoding the bank statement in Odoo, you are suggested to reconcile the transaction with the batch deposit.To enable batch deposit, module account_batch_payment must be installed.\n"
"SEPA Credit Transfer: Pay bill from a SEPA Credit Transfer file you submit to your bank. To enable sepa credit transfer, module account_sepa must be installed "
msgstr ""
"Handmatig: Krijg betaald met contant geld, cheques of een andere methode buiten Odoo om\n"
"Handmatig: Krijg betaald met contant geld, cheques of een andere methode buiten Odoo om.\n"
"Elektronisch: Betaal automatisch via een betalingsverwerker door een transactie aan te vragen op een door de klant opgeslagen kaart bij het online kopen of abonneren van (betalingstoken).\n"
"Cheque: Betaal factuur met een cheque en druk deze af in Odoo\n"
"Batch Deposito: Verzamel meerdere klantcheques tegelijk door een batch deposito te genereren om bij uw bank in te dienen. Wanneer u de bankafschriften in Odoo ingeeft, wordt u voorgesteld om de transactie af te letteren met de batchafbetaling. Schakel deze optie in bij instellingen.\n"
"SEPA overschrijving: Betaal de factuur met een SEPA verschrijvingsbestand welke je upload bij de bank. Om SEPA te activeren dient te module ccount_sepa worden geïnstalleerd."
"SEPA overschrijving: Betaal de factuur met een SEPA overschrijvingsbestand welke je upload bij de bank. Om SEPA te activeren dient te module account_sepa worden geïnstalleerd."
#. module: account
#: model:ir.model.fields,help:account.field_account_journal__outbound_payment_method_ids
@@ -7545,7 +7546,7 @@ msgid ""
"otherwise it would make a gap in the numbering. You should book an entry and"
" make a regular revert of it in case you want to cancel it."
msgstr ""
"Bewerking niet toegelaten. Aangezien u bankafschriftregel reeds een nummer "
"Bewerking niet toegelaten. Aangezien uw bankafschriftregel reeds een nummer "
"(%s) heeft gekregen, kunt u het niet compleet afletteren tegen bestaande "
"boekingen anders ontstaat er een gat in de nummering. U kan de boeking maken"
" en terugzetten wanneer u deze wil annuleren."
@@ -8377,7 +8378,7 @@ msgid ""
"Programmation Error: Can't call _get_wo_suggestion_query() for different "
"rules than 'writeoff_suggestion'"
msgstr ""
"Programmatie Error: kan niet aangeroepen worden _get_wo_suggestion_query() "
"Programmatie Error: _get_wo_suggestion_query() kan niet aangeroepen worden "
"voor een andere regel dan 'writeoff_suggestion'"
#. module: account
@@ -10381,7 +10382,7 @@ msgid ""
" don't want to reconcile all the past payments with bank statements."
msgstr ""
"De bank afletterfunctie zal niet vragen om de betalingen af te letteren welke ouder zijn dan deze datum.\n"
"Dit is zinvol als u boekhouding installeerde nadat u facturatie reeds geruime tijd in gebruik had en\n"
"Dit is zinvol als u de boekhouding applicatie installeerde nadat u facturatie reeds geruime tijd in gebruik had en\n"
"niet wil afletteren van oudere betalingen met bankafschriften."
#. module: account
@@ -10392,8 +10393,8 @@ msgid ""
" don't want to reconcile all the past payments with bank statements."
msgstr ""
"De bank afletterfunctie zal niet vragen om de betalingen af te letteren welke ouder zijn dan deze datum.\n"
"Dit is zinvol als u boekhouding installeerde nadat u facturatie reeds geruime tijd in gebruik had en\n"
"niet wil afletteren van oudere betalingen met bankafschriften."
"Dit is zinvol als u de boekhouding applicatie installeerde nadat u facturatie reeds geruime tijd in gebruik had en\n"
" oudere betalingen niet wil afletteren met bankafschriften."
#. module: account
#: model:ir.model.fields,help:account.field_account_move_line__statement_id
@@ -10710,7 +10711,7 @@ msgid ""
" * Amount Paid: Only applied when paying an amount.\n"
" * Amount Paid/Received: Applied in both cases."
msgstr ""
"Het reconsiliatie model zal enkel toegepast worden op het geselecteerde verrchting type:\n"
"Het afletter model zal enkel toegepast worden op het geselecteerde verrichting type:\n"
" * Ontvangen bedrag: Enkel toegepast indien bedrag ontvangen.\n"
" * Bedrag betaald: Enkel toegepast indien bedrag betaald.\n"
" * Bedrag betaald/ontvangen: Toegepast in beide gevallen."
@@ -10743,9 +10744,9 @@ msgid ""
" * Not Contains: Negation of \"Contains\".\n"
" * Match Regex: Define your own regular expression."
msgstr ""
"Het reconsilliatie model zal enkel toegepast worden indien de label:\n"
" * Bevatten: De voorgestelde label zal de string bevatten (hoofdletter gevoelig).\n"
" * Niet Bevatten: Negatie van \"Bevatten\".\n"
"Het aflettermodel zal enkel toegepast worden indien het label:\n"
" * Bevat: De voorgestelde label zal de string bevatten (hoofdletter gevoelig).\n"
" * Bevat niet: Negatie van \"Bevat\".\n"
" * Match Regex: Definieer uw eigen reguliere expressie."
#. module: account
@@ -10957,7 +10958,7 @@ msgid ""
"-This installs the account_batch_payment module."
msgstr ""
"Dit stelt u in staat om groepsbetalingen uit te voeren in een enkele beweging en dit vereenvoudigd het reconsilliatie proces.\n"
"- Dit installeerd de account_batch_payment module. "
"- Dit installeert de account_batch_payment module. "
#. module: account
#: model_terms:ir.ui.view,arch_db:account.res_config_settings_view_form
+30 -24
View File
@@ -187,7 +187,7 @@ msgstr ""
#. module: account
#: model_terms:ir.ui.view,arch_db:account.portal_my_home_menu_invoice
msgid "<em>Draft Invoice</em>"
msgstr ""
msgstr "<em>Factură ciornă"
#. module: account
#: model_terms:ir.ui.view,arch_db:account.account_move_line_view_kanban
@@ -215,7 +215,7 @@ msgstr ""
#. module: account
#: model_terms:ir.ui.view,arch_db:account.portal_invoice_page
msgid "<i class=\"fa fa-fw fa-comments\"/><b>Send message</b>"
msgstr ""
msgstr "<i class=\"fa fa-fw fa-comments\"/><b>Trimiteți un mesaj</b>"
#. module: account
#: model_terms:ir.ui.view,arch_db:account.view_account_journal_form
@@ -253,6 +253,9 @@ msgid ""
" aria-label=\"Opened\" title=\"Opened\" role=\"img\"/><span class=\"d-none d"
"-md-inline\"> Waiting for Payment</span></span>"
msgstr ""
"<span class=\"badge badge-pill badge-info\"><i class=\"fa fa-fw fa-clock-o\""
" aria-label=\"Opened\" title=\"Opened\" role=\"img\"/><span class=\"d-none d"
"-md-inline\">Se așteaptă plata</span></span>"
#. module: account
#: model_terms:ir.ui.view,arch_db:account.portal_my_invoices
@@ -425,7 +428,7 @@ msgstr ""
#. module: account
#: model_terms:ir.ui.view,arch_db:account.report_invoice_document
msgid "<span>Description</span>"
msgstr ""
msgstr "<span>Descriere</span>"
#. module: account
#: model_terms:ir.ui.view,arch_db:account.report_invoice_document
@@ -480,7 +483,7 @@ msgstr ""
#. module: account
#: model_terms:ir.ui.view,arch_db:account.report_invoice_document
msgid "<span>Quantity</span>"
msgstr ""
msgstr "<span>Cantitate</span>"
#. module: account
#: model_terms:ir.ui.view,arch_db:account.account_journal_dashboard_kanban_view
@@ -529,7 +532,7 @@ msgstr ""
#. module: account
#: model_terms:ir.ui.view,arch_db:account.portal_invoice_page
msgid "<strong class=\"text-muted\">Your Contact</strong>"
msgstr ""
msgstr "<strong class=\"text-muted\">Contactul dvs.</strong>"
#. module: account
#: model_terms:ir.ui.view,arch_db:account.report_invoice_document_with_payments
@@ -1125,7 +1128,7 @@ msgstr "Înregistrări contabile"
#. module: account
#: model:ir.actions.act_window,name:account.open_account_journal_dashboard_kanban
msgid "Accounting Overview"
msgstr ""
msgstr "Contabilitate generală"
#. module: account
#: model_terms:ir.ui.view,arch_db:account.view_partner_property_form
@@ -1226,7 +1229,7 @@ msgstr ""
#. module: account
#: model_terms:ir.ui.view,arch_db:account.onboarding_bank_account_step
msgid "Add a bank"
msgstr ""
msgstr "Adaugă o bancă"
#. module: account
#: model_terms:ir.actions.act_window,help:account.action_account_journal_form
@@ -1236,7 +1239,7 @@ msgstr ""
#. module: account
#: model_terms:ir.ui.view,arch_db:account.invoice_form
msgid "Add a line"
msgstr ""
msgstr "Adăugați o linie"
#. module: account
#: model_terms:ir.actions.act_window,help:account.action_account_form
@@ -1251,7 +1254,7 @@ msgstr ""
#. module: account
#: model_terms:ir.ui.view,arch_db:account.invoice_form
msgid "Add a note"
msgstr ""
msgstr "Adaugă o notă"
#. module: account
#: model_terms:ir.ui.view,arch_db:account.res_config_settings_view_form
@@ -1507,12 +1510,12 @@ msgstr "Valoare"
#. module: account
#: model:ir.model.fields,field_description:account.field_account_invoice_line__price_total
msgid "Amount (with Taxes)"
msgstr ""
msgstr "Valoare (cu taxe)"
#. module: account
#: model:ir.model.fields,field_description:account.field_account_invoice_line__price_subtotal
msgid "Amount (without Taxes)"
msgstr ""
msgstr "Valoare (fără taxe)"
#. module: account
#: model:ir.model.fields,field_description:account.field_account_bank_statement_line__amount_currency
@@ -2308,7 +2311,7 @@ msgstr "Facturile de plată"
#. module: account
#: model_terms:ir.ui.view,arch_db:account.account_journal_dashboard_kanban_view
msgid "Bills to Validate"
msgstr ""
msgstr "Facturi de validat"
#. module: account
#: code:addons/account/models/account_journal_dashboard.py:178
@@ -3051,7 +3054,7 @@ msgstr "Configurare"
#: code:addons/account/static/src/js/section_and_note_fields_backend.js:102
#, python-format
msgid "Configure a product"
msgstr ""
msgstr "Configurați un produs"
#. module: account
#: model:ir.actions.act_window,name:account.action_open_account_onboarding_invoice_layout
@@ -3314,7 +3317,7 @@ msgstr "Creează model"
#. module: account
#: selection:account.setup.bank.manual.config,create_or_link_option:0
msgid "Create new journal"
msgstr ""
msgstr "Creați un jurnal nou"
#. module: account
#: model_terms:ir.actions.act_window,help:account.rounding_list_action
@@ -3917,7 +3920,7 @@ msgstr ""
#. module: account
#: model_terms:ir.ui.view,arch_db:account.onboarding_fiscal_year_step
msgid "Define your fiscal years opening &amp; closing dates."
msgstr ""
msgstr "Definiți datele de deschidere și de închidere a anului fiscal."
#. module: account
#: model:ir.model.fields,help:account.field_account_journal__bank_statements_source
@@ -4032,7 +4035,7 @@ msgstr ""
#. module: account
#: model:ir.model,name:account.model_digest_digest
msgid "Digest"
msgstr ""
msgstr "Rezumat"
#. module: account
#: model:ir.model.fields,help:account.field_res_config_settings__group_products_in_bills
@@ -5205,12 +5208,12 @@ msgstr ""
#. module: account
#: model:ir.actions.act_window,name:account.account_invoice_import_wizard_action
msgid "Import Vendor Bills"
msgstr ""
msgstr "Importați facturile furnizorului"
#. module: account
#: model:ir.model,name:account.model_account_invoice_import_wizard
msgid "Import Your Vendor Bills from Files."
msgstr ""
msgstr "Importați facturile furnizorului din fișiere."
#. module: account
#: model:ir.model.fields,field_description:account.field_res_config_settings__module_account_bank_statement_import_csv
@@ -5629,6 +5632,8 @@ msgid ""
"Invoice_${(object.number or '').replace('/','_')}${object.state == 'draft' "
"and '_draft' or ''}"
msgstr ""
"Factură_${(object.number or '').replace('/','_')}${object.state == 'draft' "
"and '_ciornă' or ''}"
#. module: account
#: model_terms:ir.ui.view,arch_db:account.view_account_invoice_report_search
@@ -5688,7 +5693,7 @@ msgstr ""
#. module: account
#: model_terms:ir.ui.view,arch_db:account.account_journal_dashboard_kanban_view
msgid "Invoices to Validate"
msgstr ""
msgstr "Facturi de validat"
#. module: account
#: model:ir.model.fields,help:account.field_account_payment__reconciled_invoice_ids
@@ -6357,7 +6362,7 @@ msgstr ""
#. module: account
#: selection:account.setup.bank.manual.config,create_or_link_option:0
msgid "Link to an existing journal"
msgstr ""
msgstr "Legați de un jurnal existent"
#. module: account
#: model:ir.model.fields,help:account.field_account_invoice__move_id
@@ -8081,7 +8086,7 @@ msgstr "Achiziție"
#. module: account
#: model_terms:ir.ui.view,arch_db:account.invoice_supplier_form
msgid "Purchase Representative"
msgstr ""
msgstr "Responsabil achiziție"
#. module: account
#: model_terms:ir.ui.view,arch_db:account.res_config_settings_view_form
@@ -9030,17 +9035,17 @@ msgstr ""
#. module: account
#: model_terms:ir.ui.view,arch_db:account.onboarding_bank_account_step
msgid "Setup your bank account to sync bank feeds."
msgstr ""
msgstr "Configurați contul dvs. bancar pentru a sincroniza fluxurile bancare."
#. module: account
#: model_terms:ir.ui.view,arch_db:account.onboarding_chart_of_account_step
msgid "Setup your chart of accounts and record initial balances."
msgstr ""
msgstr "Configurați planul de conturi și înregistrați soldurile inițiale."
#. module: account
#: model_terms:ir.ui.view,arch_db:account.dashboard_onboarding_company_step
msgid "Setup your company's data for reports headers."
msgstr ""
msgstr "Configurați datele companiei pentru anteturile de rapoarte."
#. module: account
#: model:ir.actions.server,name:account.model_account_invoice_action_share
@@ -11519,6 +11524,7 @@ msgid ""
"You cannot change the currency of the company since some journal items "
"already exist"
msgstr ""
"Nu puteți schimba moneda companiei deoarece există deja articole de jurnale"
#. module: account
#: code:addons/account/models/account.py:317
+4 -3
View File
@@ -37,6 +37,7 @@
# Collex100, 2019
# Sergo S, 2019
# Vasiliy Korobatov <korobatov@gmail.com>, 2019
# Oleg Kuryan <oleg@ventor.tech>, 2019
#
msgid ""
msgstr ""
@@ -44,7 +45,7 @@ msgstr ""
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-01-16 07:59+0000\n"
"PO-Revision-Date: 2016-08-05 12:55+0000\n"
"Last-Translator: Vasiliy Korobatov <korobatov@gmail.com>, 2019\n"
"Last-Translator: Oleg Kuryan <oleg@ventor.tech>, 2019\n"
"Language-Team: Russian (https://www.transifex.com/odoo/teams/41243/ru/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -3693,7 +3694,7 @@ msgstr "Платежи заказчиков"
#. module: account
#: model:ir.model.fields,help:account.field_account_invoice__access_url
msgid "Customer Portal URL"
msgstr ""
msgstr "Ссылка на портал клиента"
#. module: account
#: model:ir.model.fields,field_description:account.field_product_product__taxes_id
@@ -7960,7 +7961,7 @@ msgstr ""
#. module: account
#: model:ir.model.fields,field_description:account.field_account_invoice__access_url
msgid "Portal Access URL"
msgstr ""
msgstr "Ссылка для доступа в портал"
#. module: account
#: model_terms:ir.ui.view,arch_db:account.view_move_form
+29 -17
View File
@@ -204,12 +204,12 @@ msgstr "Taslak Fatura"
#: model_terms:ir.ui.view,arch_db:account.account_move_line_view_kanban
#: model_terms:ir.ui.view,arch_db:account.view_account_move_kanban
msgid "<i class=\"fa fa-clock-o\" aria-label=\"Date\" role=\"img\" title=\"Date\"/>"
msgstr ""
msgstr "<i class=\"fa fa-clock-o\" aria-label=\"Date\" role=\"img\" title=\"Date\"/>"
#. module: account
#: model_terms:ir.ui.view,arch_db:account.view_account_payment_kanban
msgid "<i class=\"fa fa-clock-o\" role=\"img\" aria-label=\"Date\" title=\"Date\"/>"
msgstr ""
msgstr "<i class=\"fa fa-clock-o\" role=\"img\" aria-label=\"Date\" title=\"Date\"/>"
#. module: account
#: model_terms:ir.ui.view,arch_db:account.portal_invoice_page
@@ -222,6 +222,8 @@ msgid ""
"<i class=\"fa fa-ellipsis-v\" aria-label=\"Selection\" role=\"img\" "
"title=\"Selection\"/>"
msgstr ""
"<i class=\"fa fa-ellipsis-v\" aria-label=\"Selection\" role=\"img\" "
"title=\"Selection\"/>"
#. module: account
#: model_terms:ir.ui.view,arch_db:account.portal_invoice_page
@@ -264,6 +266,9 @@ msgid ""
" aria-label=\"Opened\" title=\"Opened\" role=\"img\"/><span class=\"d-none d"
"-md-inline\"> Waiting for Payment</span></span>"
msgstr ""
"<span class=\"badge badge-pill badge-info\"><i class=\"fa fa-fw fa-clock-o\""
" aria-label=\"Opened\" title=\"Opened\" role=\"img\"/><span class=\"d-none d"
"-md-inline\"> Ödeme Bekleniyor</span></span>"
#. module: account
#: model_terms:ir.ui.view,arch_db:account.portal_my_invoices
@@ -288,6 +293,9 @@ msgid ""
"specific.\" aria-label=\"Values set here are company-specific.\" "
"groups=\"base.group_multi_company\" role=\"img\"/>"
msgstr ""
"<span class=\"fa fa-lg fa-building-o\" title=\"Values set here are company-"
"specific.\" aria-label=\"Values set here are company-specific.\" "
"groups=\"base.group_multi_company\" role=\"img\"/>"
#. module: account
#: model_terms:ir.ui.view,arch_db:account.res_config_settings_view_form
@@ -295,6 +303,8 @@ msgid ""
"<span class=\"fa fa-lg fa-building-o\" title=\"Values set here are company-"
"specific.\"/>"
msgstr ""
"<span class=\"fa fa-lg fa-building-o\" title=\"Values set here are company-"
"specific.\"/>"
#. module: account
#: model_terms:ir.ui.view,arch_db:account.invoice_supplier_form
@@ -386,6 +396,8 @@ msgid ""
"<span class=\"text-muted\">Only journals not yet linked to a bank account "
"are proposed</span>"
msgstr ""
"<span class=\"text-muted\">Sadece henüz bir banka hesabına bağlı olmayan "
"yevmiye önerilmektedir</span>"
#. module: account
#: model_terms:ir.ui.view,arch_db:account.report_invoice_document
@@ -499,7 +511,7 @@ msgstr "<span>İşlemler</span>"
#. module: account
#: model_terms:ir.ui.view,arch_db:account.report_payment_receipt_document
msgid "<span>Original Amount</span>"
msgstr ""
msgstr "<span>Orijinal Tutar</span>"
#. module: account
#: model_terms:ir.ui.view,arch_db:account.report_invoice_document
@@ -686,7 +698,7 @@ msgstr "Bir Ödeme Vadesi, Bakiye tipinin sadece bir satırına sahip olmalı."
#: code:addons/account/models/account.py:861
#, python-format
msgid "A bank account can belong to only one journal."
msgstr ""
msgstr "Bir banka hesabı yalnızca bir yevmiye ait olabilir."
#. module: account
#: model_terms:ir.actions.act_window,help:account.action_bank_statement_tree
@@ -1343,7 +1355,7 @@ msgstr "Ek notlar..."
#: selection:account.tax,type_tax_use:0
#: selection:account.tax.template,type_tax_use:0
msgid "Adjustment"
msgstr ""
msgstr "Düzeltme"
#. module: account
#: model:ir.model.fields,field_description:account.field_tax_adjustments_wizard__tax_id
@@ -1717,7 +1729,7 @@ msgstr "Analitik Hesap"
#: model:ir.ui.menu,name:account.account_analytic_group_menu
#: model_terms:ir.ui.view,arch_db:account.res_config_settings_view_form
msgid "Analytic Account Groups"
msgstr ""
msgstr "Analitik Hesap Grupları"
#. module: account
#: model:ir.model.fields,field_description:account.field_res_config_settings__group_analytic_accounting
@@ -2263,7 +2275,7 @@ msgstr "Faturaya dayalı"
#: code:addons/account/models/company.py:19
#, python-format
msgid "Based on Invoice Number"
msgstr ""
msgstr "Fatura Numarasına Göre"
#. module: account
#: model:ir.model.fields,help:account.field_account_tax__tax_exigibility
@@ -2838,7 +2850,7 @@ msgstr ""
#. module: account
#: model_terms:ir.actions.act_window,help:account.actions_account_fiscal_year
msgid "Click here to create a new fiscal year."
msgstr ""
msgstr "Yeni bir mali yıl oluşturmak için buraya tıklayın."
#. module: account
#. openerp-web
@@ -2870,7 +2882,7 @@ msgstr ""
#: code:addons/account/static/src/xml/account_reconciliation.xml:17
#, python-format
msgid "Click to Rename"
msgstr ""
msgstr "Yeniden Adlandırma için Tıklayın"
#. module: account
#. openerp-web
@@ -3088,7 +3100,7 @@ msgstr "Yapılandır"
#: code:addons/account/static/src/js/section_and_note_fields_backend.js:102
#, python-format
msgid "Configure a product"
msgstr ""
msgstr "Bir Ürünü Yapılandır"
#. module: account
#: model:ir.actions.act_window,name:account.action_open_account_onboarding_invoice_layout
@@ -3218,7 +3230,7 @@ msgstr "Karşı Hesap"
#. module: account
#: model_terms:ir.ui.view,arch_db:account.view_account_reconcile_model_form
msgid "Counterpart Values"
msgstr ""
msgstr "Karşı Değerler"
#. module: account
#: model:ir.model.fields,field_description:account.field_account_fiscal_position__country_id
@@ -5277,7 +5289,7 @@ msgstr ".qif biçiminde içe aktar"
#. module: account
#: model:ir.actions.act_window,name:account.account_invoice_import_wizard_action
msgid "Import Vendor Bills"
msgstr ""
msgstr "Tedarikçi Faturalarını İçe Aktar"
#. module: account
#: model:ir.model,name:account.model_account_invoice_import_wizard
@@ -5791,7 +5803,7 @@ msgstr "Faturalama"
#: selection:account.reconcile.model,match_amount:0
#: selection:account.reconcile.model.template,match_amount:0
msgid "Is Between"
msgstr ""
msgstr "Arasında"
#. module: account
#: model:ir.model.fields,field_description:account.field_account_bank_statement__message_is_follower
@@ -5804,13 +5816,13 @@ msgstr "Takipçi mi"
#: selection:account.reconcile.model,match_amount:0
#: selection:account.reconcile.model.template,match_amount:0
msgid "Is Greater Than"
msgstr ""
msgstr "Büyüktür"
#. module: account
#: selection:account.reconcile.model,match_amount:0
#: selection:account.reconcile.model.template,match_amount:0
msgid "Is Lower Than"
msgstr ""
msgstr "Küçüktür"
#. module: account
#: model:ir.model.fields,field_description:account.field_account_invoice_send__printed
@@ -7039,7 +7051,7 @@ msgstr "Normal Borçlu"
#: selection:account.reconcile.model,match_label:0
#: selection:account.reconcile.model.template,match_label:0
msgid "Not Contains"
msgstr ""
msgstr "İçermez"
#. module: account
#: selection:res.company,account_dashboard_onboarding_state:0
@@ -8607,7 +8619,7 @@ msgstr ""
#. module: account
#: model:ir.model.fields,field_description:account.field_account_move__reverse_date
msgid "Reversal Date"
msgstr ""
msgstr "Ters Tarih"
#. module: account
#: model:ir.model.fields,field_description:account.field_account_move_reversal__date
+10 -6
View File
@@ -36,7 +36,7 @@
# sunny sun <sunxiaoxia@inspur.com>, 2018
# Maie <m@maie.name>, 2018
# liulixia <liu.lixia@elico-corp.com>, 2018
# e2f_cn r14 <e2f_cn_r14@outlook.com>, 2018
# 0023d6d916c2d56e4d76ed7a6e80ad5b, 2018
# John Lin <linyinhuan@139.com>, 2018
# xiaobin wu <bd5dml@gmail.com>, 2018
# Henry Zhou <zhouhenry@live.com>, 2018
@@ -48,10 +48,9 @@
# e2f <projects@e2f.com>, 2019
# Siyuan Gu <siyuancn@hotmail.com>, 2019
# 山西清水欧度(QQ:54773801) <54773801@qq.com>, 2019
# e2f_cn c7 <e2f_cn_c7@outlook.com>, 2019
# bf2549c5415a9287249cba2b8a5823c7, 2019
# p too <totopo@163.com>, 2019
# snow wang <147156565@qq.com>, 2019
# 开阖软件 Jeff Wang <jeff@osbzr.com>, 2019
# 苏州远鼎 <tiexinliu@126.com>, 2019
# waveyeung <waveyeung@qq.com>, 2019
# 老窦 北京 <2662059195@qq.com>, 2019
@@ -74,6 +73,7 @@
# xu xiaohu <xu.xiaohu@gmail.com>, 2019
# inspur qiuguodong <qiuguodong@inspur.com>, 2019
# Jerry Pan <panjianyu@inspur.com>, 2019
# 开阖软件 Jeff Wang <jeff@osbzr.com>, 2019
#
msgid ""
msgstr ""
@@ -81,7 +81,7 @@ msgstr ""
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-01-16 07:59+0000\n"
"PO-Revision-Date: 2016-08-05 12:55+0000\n"
"Last-Translator: Jerry Pan <panjianyu@inspur.com>, 2019\n"
"Last-Translator: 开阖软件 Jeff Wang <jeff@osbzr.com>, 2019\n"
"Language-Team: Chinese (China) (https://www.transifex.com/odoo/teams/41243/zh_CN/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -873,7 +873,7 @@ msgstr "选定的语句行已经与帐户移动相协调。"
#. module: account
#: sql_constraint:account.fiscal.position.tax:0
msgid "A tax fiscal position could be defined only one time on same taxes."
msgstr ""
msgstr "一种税只能定义一个替换规则"
#. module: account
#: model:res.groups,name:account.group_warning_account
@@ -5547,7 +5547,7 @@ msgstr "无效的\"Zip Range\",请正确的配置"
#: code:addons/account/models/company.py:106
#, python-format
msgid "Invalid fiscal year last day"
msgstr ""
msgstr "会计年度最后一天无效"
#. module: account
#: code:addons/account/models/account_invoice.py:1358
@@ -10559,6 +10559,8 @@ msgid ""
"This customer invoice credit note has been created from: <a href=# data-oe-"
"model=account.invoice data-oe-id=%d>%s</a><br>Reason: %s"
msgstr ""
"客户红字发票已生成,原发票: <a href=# data-oe-model=account.invoice data-oe-"
"id=%d>%s</a><br>原因: %s"
#. module: account
#: model_terms:ir.ui.view,arch_db:account.res_config_settings_view_form
@@ -10693,6 +10695,8 @@ msgid ""
"This vendor bill credit note has been created from: <a href=# data-oe-"
"model=account.invoice data-oe-id=%d>%s</a><br>Reason: %s"
msgstr ""
"供应商红字发票已生成。原发票:<a href=# data-oe-model=account.invoice data-oe-"
"id=%d>%s</a><br>原因: %s"
#. module: account
#: model_terms:ir.actions.act_window,help:account.action_validate_account_move
+13 -5
View File
@@ -590,13 +590,16 @@ class AccountInvoice(models.Model):
if res.get('type', False) not in ('out_invoice', 'in_refund') or not 'company_id' in res:
return res
company = self.env['res.company'].browse(res['company_id'])
if company.partner_id:
partner_bank_result = self.env['res.partner.bank'].search([('partner_id', '=', company.partner_id.id)], limit=1)
if partner_bank_result:
res['partner_bank_id'] = partner_bank_result.id
partner_bank_result = self._get_partner_bank_id(res['company_id'])
if partner_bank_result:
res['partner_bank_id'] = partner_bank_result.id
return res
def _get_partner_bank_id(self, company_id):
company = self.env['res.company'].browse(company_id)
if company.partner_id:
return self.env['res.partner.bank'].search([('partner_id', '=', company.partner_id.id)], limit=1)
@api.model
def fields_view_get(self, view_id=None, view_type='form', toolbar=False, submenu=False):
def get_view_id(xid, name):
@@ -1534,6 +1537,11 @@ class AccountInvoice(models.Model):
values['payment_term_id'] = False
values['refund_invoice_id'] = invoice.id
if values['type'] == 'in_refund':
partner_bank_result = self._get_partner_bank_id(values['company_id'])
if partner_bank_result:
values['partner_bank_id'] = partner_bank_result.id
if date:
values['date'] = date
if description:
+2 -1
View File
@@ -304,8 +304,9 @@ class AccountMove(models.Model):
@api.multi
def post(self):
self._post_validate()
# Create the analytic lines in batch is faster as it leads to less cache invalidation.
self.mapped('line_ids').create_analytic_lines()
for move in self:
move.line_ids.create_analytic_lines()
if move.name == '/':
new_name = False
journal = move.journal_id
@@ -108,3 +108,38 @@ class TestAccountSupplierInvoice(AccountingTestCase):
#I cancel the account move which is in posted state and verifies that it gives warning message
with self.assertRaises(Warning):
invoice.move_id.button_cancel()
def test_vendor_bill_refund(self):
invoice_account = self.env['account.account'].search(
[('user_type_id', '=', self.env.ref('account.data_account_type_receivable').id)], limit=1)
invoice_line_account = self.env['account.account'].search(
[('user_type_id', '=', self.env.ref('account.data_account_type_expenses').id)], limit=1)
if self.env.ref('base.main_partner').bank_account_count > 0:
bank = self.env['res.partner.bank'].search([('partner_id', '=', self.env.ref('base.main_partner').id)], limit=1)
else:
bank = self.env['res.partner.bank'].create({
'acc_number': '12345678910',
'partner_id': self.env.ref('base.main_partner').id,
})
invoice_id = self.env['account.invoice'].create({
'name': 'invoice test refund',
'partner_id': self.env.ref("base.res_partner_2").id,
'account_id': invoice_account.id,
'currency_id': self.env.ref('base.USD').id,
'type': 'in_invoice',
})
self.env['account.invoice.line'].create({
'product_id': self.env.ref("product.product_product_4").id,
'quantity': 1,
'price_unit': 15.0,
'invoice_id': invoice_id.id,
'name': 'something',
'account_id': invoice_line_account.id,
})
refund_invoices = invoice_id.refund()
self.assertEqual(refund_invoices.partner_bank_id, bank)
+9 -6
View File
@@ -7,13 +7,15 @@
# nasaaskii <nasaaskii@gmail.com>, 2018
# Khishigbat Ganbold <khishigbat@asterisk-tech.mn>, 2018
# Onii Onii <onii0223@yahoo.com>, 2018
# Baskhuu Lodoikhuu <baskhuujacara@gmail.com>, 2019
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~11.2\n"
"Project-Id-Version: Odoo Server saas~11.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-03-22 14:10+0000\n"
"PO-Revision-Date: 2018-03-22 14:10+0000\n"
"Last-Translator: Onii Onii <onii0223@yahoo.com>, 2018\n"
"POT-Creation-Date: 2018-09-18 09:49+0000\n"
"PO-Revision-Date: 2016-08-05 12:55+0000\n"
"Last-Translator: Baskhuu Lodoikhuu <baskhuujacara@gmail.com>, 2019\n"
"Language-Team: Mongolian (https://www.transifex.com/odoo/teams/41243/mn/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -48,6 +50,8 @@ msgid ""
"An analytic default requires at least an analytic account or an analytic "
"tag."
msgstr ""
"Байнга хэрэглэгдэх аналитик гэдэгт аналитик данс эсвэл аналитик пайзыг "
"хэрэглэнэ."
#. module: account_analytic_default
#: model:ir.model.fields,field_description:account_analytic_default.field_account_analytic_default__analytic_id
@@ -70,7 +74,6 @@ msgstr "Шинжилгээний тархалт"
#. module: account_analytic_default
#: model:ir.actions.act_window,name:account_analytic_default.action_product_default_list
#: model:ir.actions.act_window,name:account_analytic_default.analytic_rule_action_partner
#: model:ir.actions.act_window,name:account_analytic_default.analytic_rule_action_user
msgid "Analytic Rules"
msgstr "Шинжилгээний дүрмүүд"
@@ -104,7 +107,7 @@ msgstr "Үүсгэсэн"
#. module: account_analytic_default
#: model_terms:ir.ui.view,arch_db:account_analytic_default.res_config_settings_view_form
msgid "Default Analytic Values"
msgstr ""
msgstr "Шинжилгээний анхны утгууд"
#. module: account_analytic_default
#: model:ir.model.fields,help:account_analytic_default.field_account_analytic_default__date_stop
@@ -24,6 +24,7 @@
# Jesús Alan Ramos Rodríguez <alan.ramos@jarsa.com.mx>, 2018
# Pedro M. Baeza <pedro.baeza@gmail.com>, 2019
# Gabriel Umaña <gabriel.umana@delfixcr.com>, 2019
# Jesse Garza <jga@odoo.com>, 2019
#
msgid ""
msgstr ""
@@ -31,7 +32,7 @@ msgstr ""
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-09-21 13:17+0000\n"
"PO-Revision-Date: 2016-08-05 12:55+0000\n"
"Last-Translator: Gabriel Umaña <gabriel.umana@delfixcr.com>, 2019\n"
"Last-Translator: Jesse Garza <jga@odoo.com>, 2019\n"
"Language-Team: Spanish (https://www.transifex.com/odoo/teams/41243/es/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -160,7 +161,7 @@ msgstr "Línea de extracto bancario"
#. module: account_bank_statement_import
#: model:ir.model,name:account_bank_statement_import.model_account_setup_bank_manual_config
msgid "Bank setup manual config"
msgstr ""
msgstr "Configuración manual de datos bancarios"
#. module: account_bank_statement_import
#: model:ir.model.fields,field_description:account_bank_statement_import.field_account_bank_statement_import_journal_creation__belongs_to_company
+25 -10
View File
@@ -82,7 +82,7 @@ msgstr "Хуурмаг нэр"
#. module: account_bank_statement_import
#: model:ir.model.fields,field_description:account_bank_statement_import.field_account_bank_statement_import_journal_creation__alias_name
msgid "Alias Name for Vendor Bills"
msgstr ""
msgstr "Нийлүүлэгчийн нэхэмжлэлийн имэйл нэр"
#. module: account_bank_statement_import
#: model:ir.model.fields,field_description:account_bank_statement_import.field_account_bank_statement_import_journal_creation__alias_domain
@@ -103,12 +103,12 @@ msgstr "Аль хэдийнээ импорт хийгдсэн мөрүүд"
#. module: account_bank_statement_import
#: model:ir.model.fields,field_description:account_bank_statement_import.field_account_bank_statement_import_journal_creation__at_least_one_inbound
msgid "At Least One Inbound"
msgstr ""
msgstr "Төлбөр хүлээн авах сувагтай"
#. module: account_bank_statement_import
#: model:ir.model.fields,field_description:account_bank_statement_import.field_account_bank_statement_import_journal_creation__at_least_one_outbound
msgid "At Least One Outbound"
msgstr ""
msgstr "Төлбөр илгээх сувагтай"
#. module: account_bank_statement_import
#: code:addons/account_bank_statement_import/account_bank_statement_import.py:82
@@ -145,7 +145,7 @@ msgstr "Банкны хуулгын мөр"
#. module: account_bank_statement_import
#: model:ir.model,name:account_bank_statement_import.model_account_setup_bank_manual_config
msgid "Bank setup manual config"
msgstr ""
msgstr "Банкны данс тохируулга"
#. module: account_bank_statement_import
#: model:ir.model.fields,field_description:account_bank_statement_import.field_account_bank_statement_import_journal_creation__belongs_to_company
@@ -174,6 +174,8 @@ msgid ""
"Check this box if you don't want to share the same sequence for invoices and"
" credit notes made from this journal"
msgstr ""
"Үүнийг сонгосноор энэхүү журналд бүртгэгдэх нэхэмжлэл болон буцаалтууд нь "
"дахин давтагдашгүй дугаарлалттай байх болно"
#. module: account_bank_statement_import
#: model:ir.model.fields,help:account_bank_statement_import.field_account_bank_statement_import_journal_creation__update_posted
@@ -244,7 +246,7 @@ msgstr "Валют"
#. module: account_bank_statement_import
#: model:ir.model.fields,field_description:account_bank_statement_import.field_account_bank_statement_import_journal_creation__refund_sequence
msgid "Dedicated Credit Note Sequence"
msgstr ""
msgstr "Буцаалтын тусдаа дугаарлалт"
#. module: account_bank_statement_import
#: model:ir.model.fields,field_description:account_bank_statement_import.field_account_bank_statement_import_journal_creation__default_credit_account_id
@@ -259,7 +261,7 @@ msgstr "Үндсэн дебит данс"
#. module: account_bank_statement_import
#: model:ir.model.fields,help:account_bank_statement_import.field_account_bank_statement_import_journal_creation__bank_statements_source
msgid "Defines how the bank statements will be registered"
msgstr ""
msgstr "Банкны хуулга хэрхэн бүртгэгдэхийг тодорхойлно"
#. module: account_bank_statement_import
#: model:ir.model.fields,field_description:account_bank_statement_import.field_account_bank_statement_import__display_name
@@ -368,7 +370,7 @@ msgstr "Энэ бол дебит гүйлгээний үндсэн данс"
#. module: account_bank_statement_import
#: model:ir.model.fields,help:account_bank_statement_import.field_account_bank_statement_import_journal_creation__alias_name
msgid "It creates draft vendor bill by sending an email."
msgstr ""
msgstr "Имэйл илгээснээр нийлүүлэгчийн ноорог нэхэмжлэл бүртгэгдэнэ."
#. module: account_bank_statement_import
#: model:ir.model,name:account_bank_statement_import.model_account_journal
@@ -442,6 +444,9 @@ msgid ""
"Electronic: Get paid automatically through a payment acquirer by requesting a transaction on a card saved by the customer when buying or subscribing online (payment token).\n"
"Batch Deposit: Encase several customer checks at once by generating a batch deposit to submit to your bank. When encoding the bank statement in Odoo,you are suggested to reconcile the transaction with the batch deposit. Enable this option from the settings."
msgstr ""
"Гараар: Бэлэн мөнгөөр төлөгдөх эсвэл Odoo системээс гадуур ямар нэг төлбөрийн хэрэгслээр төлөгдөх.\n"
"Электрон: Ямар нэг онлайн төлбөрийн хэрэгсэлээр төлөлт хийх үед автоматаар захиалагчийн гүйлгээ бүртгэгдэх (төлбөрийн token).\n"
"Багц төлбөр: Захиалагч хэд хэдэн төлөлтийг нэгтгэн банкнаар нэг төлбөр шилжүүлэх тохиолдол. Odoo-д банкны гүйлгээ хөтлөгдөх үед танд холбогдох нэхэмжлэлүүдийг сонгож тулгах боломж олгогдоно. Энэ боломжыг тохиргооноос идэвхижүүлээрэй."
#. module: account_bank_statement_import
#: model:ir.model.fields,help:account_bank_statement_import.field_account_bank_statement_import_journal_creation__outbound_payment_method_ids
@@ -450,6 +455,9 @@ msgid ""
"Check:Pay bill by check and print it from Odoo.\n"
"SEPA Credit Transfer: Pay bill from a SEPA Credit Transfer file you submit to your bank. Enable this option from the settings."
msgstr ""
"Гараар: Бэлэн мөнгөөр төлөгдөх эсвэл Odoo системээс гадуур ямар нэг төлбөрийн хэрэгслээр төлөгдөх.\n"
"Чек: Нэхэмжлэлийг чекээр төлж төлбөрийн баримтыг Odoo-с хэвлэх.\n"
"SEPA Credit Transfer: SEPA Credit Transfer систем ашиглан нэхэмжлэлийн төлбөрийг банк руу дамжуулах. Үүний тулд account_sepa модуль суулгах шаардлагатай. "
#. module: account_bank_statement_import
#: model:ir.model.fields,field_description:account_bank_statement_import.field_account_bank_statement_import_journal_creation__sequence_number_next
@@ -470,7 +478,7 @@ msgstr "OK"
#. module: account_bank_statement_import
#: model:ir.model.fields,field_description:account_bank_statement_import.field_account_bank_statement_import_journal_creation__post_at_bank_rec
msgid "Post At Bank Reconciliation"
msgstr ""
msgstr "Банкны тулгалтаар баталгаажуулах"
#. module: account_bank_statement_import
#: model:ir.model.fields,field_description:account_bank_statement_import.field_account_bank_statement_import_journal_creation__profit_account_id
@@ -504,6 +512,8 @@ msgstr "Дараалал"
#: model:ir.model.fields,help:account_bank_statement_import.field_account_bank_statement_import_journal_creation__active
msgid "Set active to false to hide the Journal without removing it."
msgstr ""
"Идэвхитэй талбарыг үгүй утгаар тэмдэглэснээр журналыг устгалгүйгээр нуух "
"боломжтой."
#. module: account_bank_statement_import
#: model:ir.model.fields,field_description:account_bank_statement_import.field_account_bank_statement_import_journal_creation__code
@@ -553,12 +563,12 @@ msgstr "Энэ журналийн бичилтүүдийн нэр нь энэ у
#. module: account_bank_statement_import
#: model:ir.model.fields,help:account_bank_statement_import.field_account_bank_statement_import_journal_creation__refund_sequence_number_next
msgid "The next sequence number will be used for the next credit note."
msgstr ""
msgstr "Дараагийн буцаалтын нэхэмжлэл дээр ашиглагдах дараагийн дугаар"
#. module: account_bank_statement_import
#: model:ir.model.fields,help:account_bank_statement_import.field_account_bank_statement_import_journal_creation__sequence_number_next
msgid "The next sequence number will be used for the next invoice."
msgstr ""
msgstr "Дараагийн нэхэмжлэл дээр ашиглагдах дараагийн дугаар"
#. module: account_bank_statement_import
#: model:ir.model.fields,help:account_bank_statement_import.field_account_bank_statement_import_journal_creation__refund_sequence_id
@@ -566,6 +576,8 @@ msgid ""
"This field contains the information related to the numbering of the credit "
"note entries of this journal."
msgstr ""
"Энэ талбар нь уг журналд хичнээн буцаалтын нэхэмжлэл бүртгэгдсэн тухай "
"мэдээллийг агуулна."
#. module: account_bank_statement_import
#: model:ir.model.fields,help:account_bank_statement_import.field_account_bank_statement_import_journal_creation__sequence_id
@@ -623,6 +635,9 @@ msgid ""
"draft state, so that the related journal entries are only posted when "
"performing bank reconciliation."
msgstr ""
"Энэ журналд бүртгэгдсэн төлбөр төлөлтийн гүйлгээнүүд ноорог төлөвтэй байх ба"
" банкны хуулгатай ижилсүүлэлт буюу тулгалт хийсний эцэст батлагдсан төлөвт "
"шилжинэ."
#. module: account_bank_statement_import
#: model:ir.model.fields,help:account_bank_statement_import.field_account_bank_statement_import_journal_creation__show_on_dashboard
@@ -451,7 +451,7 @@ msgid ""
"Electronic: Get paid automatically through a payment acquirer by requesting a transaction on a card saved by the customer when buying or subscribing online (payment token).\n"
"Batch Deposit: Encase several customer checks at once by generating a batch deposit to submit to your bank. When encoding the bank statement in Odoo,you are suggested to reconcile the transaction with the batch deposit. Enable this option from the settings."
msgstr ""
"Handmatig: Krijg betaald met contant geld, cheques of een andere methode buiten Odoo om\n"
"Handmatig: Krijg betaald met contant geld, cheques of een andere methode buiten Odoo om.\n"
"Elektronisch: Betaal automatisch via een betalingsverwerver door een transactie aan te vragen op een door de klant opgeslagen kaart bij het online kopen of abonneren van (betalingstoken).\n"
"Batch Deposito: Verzamel meerdere klantcheques tegelijk door een batch deposito te genereren om bij uw bank in te dienen. Wanneer u de bankafschriften in Odoo ingeeft, wordt u voorgesteld om de transactie af te letteren met de batchafbetaling. Schakel deze optie in bij instellingen."
+1 -1
View File
@@ -68,7 +68,7 @@ msgstr "Скасувати"
#: model:ir.model.fields,field_description:account_check_printing.field_res_company__account_check_printing_layout
#: model:ir.model.fields,field_description:account_check_printing.field_res_config_settings__account_check_printing_layout
msgid "Check Layout"
msgstr "Перевірити компонування"
msgstr "Шаблон чеку"
#. module: account_check_printing
#: model:ir.model.fields,field_description:account_check_printing.field_res_company__account_check_printing_margin_left
+10 -4
View File
@@ -3,14 +3,17 @@
# * account_payment
#
# Translators:
# Martin Trigaux, 2018
# Cozmin Candea <office@terrabit.ro>, 2018
# Martin Trigaux, 2019
# Dorin Hongu <dhongu@gmail.com>, 2019
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~11.5\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-09-18 09:49+0000\n"
"PO-Revision-Date: 2018-09-18 09:49+0000\n"
"Last-Translator: Martin Trigaux, 2018\n"
"PO-Revision-Date: 2017-12-13 14:50+0000\n"
"Last-Translator: Dorin Hongu <dhongu@gmail.com>, 2019\n"
"Language-Team: Romanian (https://www.transifex.com/odoo/teams/41243/ro/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -71,6 +74,9 @@ msgid ""
"clock-o\"/><span class=\"d-none d-md-inline\"> Waiting for "
"Payment</span></span>"
msgstr ""
"<span class=\"badge badge-pill badge-info\"><i class=\"fa fa-fw fa-"
"clock-o\"/><span class=\"d-none d-md-inline\">Se așteaptă "
"plata</span></span>"
#. module: account_payment
#: model_terms:ir.ui.view,arch_db:account_payment.portal_my_invoices_payment
@@ -128,7 +134,7 @@ msgstr ""
#. module: account_payment
#: model_terms:ir.ui.view,arch_db:account_payment.portal_invoice_payment
msgid "Pay with"
msgstr ""
msgstr "Plata cu"
#. module: account_payment
#: model:ir.model,name:account_payment.model_payment_transaction
+1 -1
View File
@@ -696,4 +696,4 @@ msgstr ""
#: code:addons/account_voucher/models/account_voucher.py:457
#, python-format
msgid "You must first select a partner."
msgstr ""
msgstr "Та эхлээд харилцагч сонгох ёстой."
+1 -1
View File
@@ -68,7 +68,7 @@ msgstr "Muhasebe Fişi"
#. module: account_voucher
#: model:ir.model,name:account_voucher.model_account_voucher_line
msgid "Accounting Voucher Line"
msgstr ""
msgstr "Muhasebe Makbuz Satırı"
#. module: account_voucher
#: model:ir.model.fields,field_description:account_voucher.field_account_voucher__message_needaction
+3 -3
View File
@@ -44,7 +44,7 @@ msgstr ""
#. module: analytic
#: model_terms:ir.actions.act_window,help:analytic.account_analytic_tag_action
msgid "Add a new tag"
msgstr ""
msgstr "Шинэ пайз нэмэх"
#. module: analytic
#: model:ir.model.fields,field_description:analytic.field_account_analytic_line__amount
@@ -73,7 +73,7 @@ msgstr ""
#: model_terms:ir.ui.view,arch_db:analytic.account_analytic_group_form_view
#: model_terms:ir.ui.view,arch_db:analytic.account_analytic_group_tree_view
msgid "Analytic Account Groups"
msgstr ""
msgstr "Шинжилгээний дансны бүлэг"
#. module: analytic
#: model:res.groups,name:analytic.group_analytic_accounting
@@ -450,7 +450,7 @@ msgstr "Эцэг"
#. module: analytic
#: model:ir.model.fields,field_description:analytic.field_account_analytic_group__parent_path
msgid "Parent Path"
msgstr ""
msgstr "Эцэг зам"
#. module: analytic
#: model:ir.model.fields,field_description:analytic.field_account_analytic_distribution__tag_id
+1 -1
View File
@@ -80,7 +80,7 @@ msgstr ""
#: model_terms:ir.ui.view,arch_db:analytic.account_analytic_group_form_view
#: model_terms:ir.ui.view,arch_db:analytic.account_analytic_group_tree_view
msgid "Analytic Account Groups"
msgstr ""
msgstr "Analitik Hesap Grupları"
#. module: analytic
#: model:res.groups,name:analytic.group_analytic_accounting
+17 -20
View File
@@ -56,20 +56,18 @@ var BarcodeEvents = core.Class.extend(mixins.PropertiesMixin, {
this.isChromeMobile = isMobile && navigator.userAgent.match(/Chrome/i);
// Creates an input who will receive the barcode scanner value.
if (this.isChromeMobile) {
this.$barcodeInput = $('<input/>', {
name: 'barcode',
type: 'text',
css: {
'position': 'fixed',
'top': '50%',
'transform': 'translateY(-50%)',
'z-index': '-1',
},
});
// Avoid to show autocomplete for a non appearing input
this.$barcodeInput.attr('autocomplete', 'off');
}
this.$barcodeInput = $('<input/>', {
name: 'barcode',
type: 'text',
css: {
'position': 'fixed',
'top': '50%',
'transform': 'translateY(-50%)',
'z-index': '-1',
},
});
// Avoid to show autocomplete for a non appearing input
this.$barcodeInput.attr('autocomplete', 'off');
this.__blurBarcodeInput = _.debounce(this._blurBarcodeInput, this.inputTimeOut);
},
@@ -220,7 +218,8 @@ var BarcodeEvents = core.Class.extend(mixins.PropertiesMixin, {
* @param {jQuery.Event} e keydown event
*/
_listenBarcodeScanner: function (e) {
if (!$('input:text:focus, input[type=number]:focus, textarea:focus, [contenteditable]:focus').length) {
if ($(document.activeElement).not('input:text, textarea, [contenteditable], ' +
'[type="email"], [type="number"], [type="password"], [type="tel"]').length) {
$('body').append(this.$barcodeInput);
this.$barcodeInput.focus();
}
@@ -263,11 +262,9 @@ var BarcodeEvents = core.Class.extend(mixins.PropertiesMixin, {
* @private
*/
_blurBarcodeInput: function () {
if (this.$barcodeInput) {
// Close the virtual keyboard on mobile browsers
// FIXME: actually we can't prevent keyboard from opening
this.$barcodeInput.val('').blur();
}
// Close the virtual keyboard on mobile browsers
// FIXME: actually we can't prevent keyboard from opening
this.$barcodeInput.val('').blur();
},
start: function(prevent_key_repeat){
@@ -0,0 +1,74 @@
odoo.define('barcodes.barcode_mobile_tests', function () {
"use strict";
QUnit.module('Barcodes', {}, function () {
QUnit.module('Barcodes Mobile');
QUnit.test('barcode field automatically focus behavior', function (assert) {
assert.expect(10);
// Mock Chrome mobile environment
var barcodeEvents = odoo.__DEBUG__.services["barcodes.BarcodeEvents"].BarcodeEvents;
var __isChromeMobile = barcodeEvents.isChromeMobile;
barcodeEvents.isChromeMobile = true;
// Rebind keyboard events
barcodeEvents.stop();
barcodeEvents.start();
var $form = $(
'<form>' +
'<input name="email" type="email"/>' +
'<input name="number" type="number"/>' +
'<input name="password" type="password"/>' +
'<input name="tel" type="tel"/>' +
'<input name="text"/>' +
'<input name="explicit_text" type="text"/>' +
'<textarea></textarea>' +
'<div contenteditable="true"></div>' +
'<select name="select">' +
'<option value="option1">Option 1</option>' +
'<option value="option2">Option 2</option>' +
'</select>' +
'</form>');
$('#qunit-fixture').append($form);
// Some elements doesn't need to keep the focus
$('body').keydown();
assert.strictEqual(document.activeElement.name, 'barcode',
"hidden barcode input should have the focus");
var $element = $form.find('select');
$element.focus().keydown();
assert.strictEqual(document.activeElement.name, 'barcode',
"hidden barcode input should have the focus");
// Those elements absolutely need to keep the focus:
// inputs elements:
var keepFocusedElements = ['email', 'number', 'password', 'tel',
'text', 'explicit_text'];
for (var i = 0; i < keepFocusedElements.length; ++i) {
$element = $form.find('input[name=' + keepFocusedElements[i] + ']');
$element.focus().keydown();
assert.strictEqual(document.activeElement, $element[0],
"input " + keepFocusedElements[i] + " should keep focus");
}
// textarea element
$element = $form.find('textarea');
$element.focus().keydown();
assert.strictEqual(document.activeElement, $element[0],
"textarea should keep focus");
// contenteditable elements
$element = $form.find('[contenteditable=true]');
$element.focus().keydown();
assert.strictEqual(document.activeElement, $element[0],
"contenteditable should keep focus");
$('#qunit-fixture').empty();
barcodeEvents.isChromeMobile = __isChromeMobile;
// Rebind keyboard events
barcodeEvents.stop();
barcodeEvents.start();
});
});
});
@@ -16,4 +16,10 @@
</xpath>
</template>
<template id="qunit_mobile_suite" name="barcode_mobile_tests" inherit_id="web.qunit_mobile_suite">
<xpath expr="//t[@t-set='head']" position="inside">
<script type="text/javascript" src="/barcodes/static/tests/barcode_mobile_tests.js"></script>
</xpath>
</template>
</odoo>
+4 -1
View File
@@ -9,7 +9,7 @@ import math
from datetime import datetime, time
from pytz import timezone, UTC
from odoo import api, fields, models, tools
from odoo import api, fields, models, tools, SUPERUSER_ID
from odoo.addons.resource.models.resource import float_to_time, HOURS_PER_DAY
from odoo.exceptions import AccessError, UserError, ValidationError
from odoo.tools import float_compare
@@ -745,6 +745,9 @@ class HolidaysRequest(models.Model):
def _check_approval_update(self, state):
""" Check if target state is achievable. """
if self.env.user.id == SUPERUSER_ID:
return
current_employee = self.env['hr.employee'].search([('user_id', '=', self.env.uid)], limit=1)
is_team_leader = self.env.user.has_group('hr_holidays.group_hr_holidays_team_leader')
is_officer = self.env.user.has_group('hr_holidays.group_hr_holidays_user')
+8 -8
View File
@@ -143,7 +143,9 @@ class HrBenefit(models.Model):
r['employee_id'][0],
r['benefit_type_id'][0] if r['benefit_type_id'] else False,
) for r in month_recs}
new_vals = [v for v in vals_list if (v['date_start'].replace(tzinfo=None), v['date_stop'].replace(tzinfo=None), v['employee_id'], v['benefit_type_id']) not in existing_entries]
assert all(v['date_start'].tzinfo is None for v in vals_list)
assert all(v['date_stop'].tzinfo is None for v in vals_list)
new_vals = [v for v in vals_list if (v['date_start'], v['date_stop'], v['employee_id'], v['benefit_type_id']) not in existing_entries]
# Remove duplicates from vals_list, shouldn't be necessary from saas-12.2
unique_new_vals = set()
for values in new_vals:
@@ -188,8 +190,6 @@ class HrBenefit(models.Model):
if benefit.date_start.date() == benefit.date_stop.date():
new_benefits |= benefit
else:
tz = pytz.timezone(benefit.employee_id.tz)
benefit_start, benefit_stop = tz.localize(benefit.date_start), tz.localize(benefit.date_stop)
values = {
'name': benefit.name,
'employee_id': benefit.employee_id.id,
@@ -198,9 +198,9 @@ class HrBenefit(models.Model):
}
benefit_state = benefit.state
benefits_to_unlink |= benefit
for start, stop in _split_range_by_day(benefit_start, benefit_stop):
values['date_start'] = start.astimezone(pytz.utc)
values['date_stop'] = stop.astimezone(pytz.utc)
for start, stop in _split_range_by_day(benefit.date_start, benefit.date_stop):
values['date_start'] = start
values['date_stop'] = stop
new_benefit = self.create(values)
# Write the state after the creation due to the ir.rule on benefit state
new_benefit.state = benefit_state
@@ -249,8 +249,8 @@ class HrBenefit(models.Model):
def _duplicate_to_calendar_attendance(self):
mapped_data = {
benefit: [
pytz.utc.localize(benefit.date_start).astimezone(pytz.timezone(benefit.employee_id.tz)), # Start date
pytz.utc.localize(benefit.date_stop).astimezone(pytz.timezone(benefit.employee_id.tz)) # End date
benefit.date_start,
benefit.date_stop,
] for benefit in self
}
+12 -20
View File
@@ -1,11 +1,11 @@
# -*- coding:utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, fields, models, _
from odoo.addons.resource.models.resource import Intervals
from datetime import datetime, timedelta
from datetime import datetime
import pytz
from odoo import api, fields, models
class HrEmployee(models.Model):
_inherit = 'hr.employee'
@@ -44,17 +44,10 @@ class HrEmployee(models.Model):
@api.model
def generate_benefit(self, date_start, date_stop):
def _format_datetime(date):
fmt = '%Y-%m-%d %H:%M:%S'
date = datetime.strptime(date, fmt) if isinstance(date, str) else date
return date.replace(tzinfo=pytz.utc) if not date.tzinfo else date
date_start = fields.Datetime.to_datetime(date_start)
date_stop = fields.Datetime.to_datetime(date_stop)
attendance_type = self.env.ref('hr_payroll.benefit_type_attendance')
vals_list = []
leaves = self.env['hr.leave']
date_start = _format_datetime(date_start)
date_stop = _format_datetime(date_stop)
current_contracts = self.env['hr.employee']._get_all_contracts(date_start, date_stop, states=['open', 'pending', 'close'])
current_employees = current_contracts.mapped('employee_id')
@@ -64,13 +57,12 @@ class HrEmployee(models.Model):
mapped_data[contract.employee_id] |= contract
for employee, contracts in mapped_data.items():
# Approved leaves
emp_leaves = employee.resource_calendar_id.leave_ids.filtered(
lambda r:
r.resource_id == employee.resource_id and
r.date_from.replace(tzinfo=pytz.utc) <= date_stop and
r.date_to.replace(tzinfo=pytz.utc) >= date_start
r.date_from <= date_stop and
r.date_to >= date_start
)
global_leaves = employee.resource_calendar_id.global_leave_ids
@@ -78,9 +70,8 @@ class HrEmployee(models.Model):
vals_list.extend(employee_leaves._get_benefits_values())
for contract in contracts:
date_start_benefits = max(date_start, datetime.combine(contract.date_start, datetime.min.time()).replace(tzinfo=pytz.utc))
date_stop_benefits = min(date_stop, datetime.combine(contract.date_end or datetime.max.date(), datetime.max.time()).replace(tzinfo=pytz.utc))
date_start_benefits = max(date_start, fields.Datetime.to_datetime(contract.date_start)).replace(tzinfo=pytz.utc)
date_stop_benefits = min(date_stop, datetime.combine(contract.date_end or datetime.max.date(), datetime.max.time())).replace(tzinfo=pytz.utc)
calendar = contract.resource_calendar_id
resource = employee.resource_id
@@ -88,10 +79,11 @@ class HrEmployee(models.Model):
# Attendances
for interval in attendances:
benefit_type_id = interval[2].mapped('benefit_type_id')[:1] or attendance_type
# All benefits generated here are using datetimes converted from the employee's timezone
vals_list += [{
'name': "%s: %s" % (benefit_type_id.name, employee.name),
'date_start': interval[0].astimezone(pytz.utc),
'date_stop': interval[1].astimezone(pytz.utc),
'date_start': interval[0].astimezone(pytz.utc).replace(tzinfo=None),
'date_stop': interval[1].astimezone(pytz.utc).replace(tzinfo=None),
'benefit_type_id': benefit_type_id.id,
'employee_id': employee.id,
'contract_id': contract.id,
+50 -70
View File
@@ -1,8 +1,10 @@
# # -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import pytz
from datetime import datetime
from dateutil.relativedelta import relativedelta
import pytz
from odoo.fields import Datetime, Date
from odoo.tests.common import tagged
from odoo.addons.hr_payroll.tests.common import TestPayslipBase
@@ -10,15 +12,14 @@ from odoo.addons.hr_payroll.tests.common import TestPayslipBase
@tagged('benefit')
class TestBenefit(TestPayslipBase):
def setUp(self):
super(TestBenefit, self).setUp()
self.tz = pytz.timezone(self.richard_emp.tz)
self.start = self.to_datetime_tz('2015-11-01 01:00:00')
self.end = self.to_datetime_tz('2015-11-30 23:59:59')
self.start = datetime(2015, 11, 1, 1, 0, 0)
self.end = datetime(2015, 11, 30, 23, 59, 59)
self.resource_calendar_id = self.env['resource.calendar'].create({'name': 'Zboub'})
contract = self.env['hr.contract'].create({
'date_start': self.start - relativedelta(days=5),
'date_start': self.start.date() - relativedelta(days=5),
'name': 'dodo',
'resource_calendar_id': self.resource_calendar_id.id,
'wage': 1000,
@@ -34,24 +35,6 @@ class TestBenefit(TestPayslipBase):
'code': 'WORK200'
})
def to_datetime_tz(self, datetime_str, tz=None):
tz = tz or self.tz
return tz.localize(Datetime.to_datetime(datetime_str))
def assertDatetimeTzEqual(self, value, target, tz=None):
"""
Assert equality between two dates.
:param value: timezone naive datetime
:param target: datetime string
:param tz: timezone to interpret the tartget string
:raises AssertionError: raises exception if the two dates are not equal
"""
tz = tz or self.tz
self.assertEqual(
pytz.utc.localize(value).astimezone(tz),
self.to_datetime_tz(target, tz=tz)
)
def test_no_duplicate(self):
self.richard_emp.generate_benefit(self.start, self.end)
pou1 = self.env['hr.benefit'].search_count([])
@@ -60,16 +43,14 @@ class TestBenefit(TestPayslipBase):
self.assertEqual(pou1, pou2, "Benefits should not be duplicated")
def test_benefit(self):
self.richard_emp.generate_benefit(self.start, self.end)
attendance_nb = len(self.resource_calendar_id._attendance_intervals(self.start, self.end))
attendance_nb = len(self.resource_calendar_id._attendance_intervals(self.start.replace(tzinfo=pytz.utc), self.end.replace(tzinfo=pytz.utc)))
benefit_nb = self.env['hr.benefit'].search_count([('employee_id', '=', self.richard_emp.id)])
self.assertEqual(attendance_nb, benefit_nb, "One benefit should be generated for each calendar attendance")
def test_split_benefit_by_day(self):
start = self.to_datetime_tz('2015-11-01 09:00:00')
end = self.to_datetime_tz('2015-11-03 18:00:00')
start = datetime(2015, 11, 1, 9, 0, 0)
end = datetime(2015, 11, 3, 18, 0, 0)
# Benefit of type attendance should be split in three
benefit = self.env['hr.benefit'].create({
@@ -83,18 +64,18 @@ class TestBenefit(TestPayslipBase):
benefits = benefit._split_by_day()
self.assertEqual(len(benefits), 3, "Benefit should be split in three")
self.assertDatetimeTzEqual(benefits[0].date_start, '2015-11-01 09:00:00')
self.assertDatetimeTzEqual(benefits[0].date_stop, '2015-11-01 23:59:59')
self.assertEqual(benefits[0].date_start, datetime(2015, 11, 1, 9, 0, 0))
self.assertEqual(benefits[0].date_stop, datetime(2015, 11, 1, 23, 59, 59))
self.assertDatetimeTzEqual(benefits[1].date_start, '2015-11-02 00:00:00')
self.assertDatetimeTzEqual(benefits[1].date_stop, '2015-11-02 23:59:59')
self.assertEqual(benefits[1].date_start, datetime(2015, 11, 2, 0, 0, 0))
self.assertEqual(benefits[1].date_stop, datetime(2015, 11, 2, 23, 59, 59))
self.assertDatetimeTzEqual(benefits[2].date_start, '2015-11-03 00:00:00')
self.assertDatetimeTzEqual(benefits[2].date_stop, '2015-11-03 18:00:00')
self.assertEqual(benefits[2].date_start, datetime(2015, 11, 3, 0, 0, 0))
self.assertEqual(benefits[2].date_stop, datetime(2015, 11, 3, 18, 0, 0))
# Test with end at mid-night -> should not create benefit starting and ending at the same time (at 00:00)
start = self.to_datetime_tz('2013-11-01 00:00:00')
end = self.to_datetime_tz('2013-11-04 00:00:00')
start = datetime(2013, 11, 1, 0, 0, 0)
end = datetime(2013, 11, 4, 0, 0, 0)
benefit = self.env['hr.benefit'].create({
'name': '1',
@@ -105,19 +86,18 @@ class TestBenefit(TestPayslipBase):
})
benefits = benefit._split_by_day()
self.assertEqual(len(benefits), 3, "Benefit should be split in three")
self.assertDatetimeTzEqual(benefits[0].date_start, '2013-11-01 00:00:00')
self.assertDatetimeTzEqual(benefits[0].date_stop, '2013-11-01 23:59:59')
self.assertEqual(benefits[0].date_start, datetime(2013, 11, 1, 0, 0, 0))
self.assertEqual(benefits[0].date_stop, datetime(2013, 11, 1, 23, 59, 59))
self.assertDatetimeTzEqual(benefits[1].date_start, '2013-11-02 00:00:00')
self.assertDatetimeTzEqual(benefits[1].date_stop, '2013-11-02 23:59:59')
self.assertEqual(benefits[1].date_start, datetime(2013, 11, 2, 0, 0, 0))
self.assertEqual(benefits[1].date_stop, datetime(2013, 11, 2, 23, 59, 59))
self.assertDatetimeTzEqual(benefits[2].date_start, '2013-11-03 00:00:00')
self.assertDatetimeTzEqual(benefits[2].date_stop, '2013-11-03 23:59:59')
self.assertEqual(benefits[2].date_start, datetime(2013, 11, 3, 0, 0, 0))
self.assertEqual(benefits[2].date_stop, datetime(2013, 11, 3, 23, 59, 59))
def test_approve_multiple_day_benefit(self):
start = self.to_datetime_tz('2015-11-01 09:00:00')
end = self.to_datetime_tz('2015-11-03 18:00:00')
start = datetime(2015, 11, 1, 9, 0, 0)
end = datetime(2015, 11, 3, 18, 0, 0)
# Benefit of type attendance should be split in three
benefit = self.env['hr.benefit'].create({
@@ -134,8 +114,8 @@ class TestBenefit(TestPayslipBase):
self.assertEqual(len(benefits), 3, "Benefit should be split in three")
def test_duplicate_global_benefit_to_attendance(self):
start = self.to_datetime_tz('2015-11-01 09:00:00')
end = self.to_datetime_tz('2015-11-03 18:00:00')
start = datetime(2015, 11, 1, 9, 0, 0)
end = datetime(2015, 11, 3, 18, 0, 0)
benef = self.env['hr.benefit'].create({
'name': '1',
@@ -153,8 +133,8 @@ class TestBenefit(TestPayslipBase):
self.assertEqual(attendance_nb, 0, "It should not duplicate the 'normal/global' benefit type")
def test_duplicate_benefit_to_attendance(self):
start = self.to_datetime_tz('2015-11-01 09:00:00')
end = self.to_datetime_tz('2015-11-03 18:00:00')
start = datetime(2015, 11, 1, 9, 0, 0)
end = datetime(2015, 11, 3, 18, 0, 0)
# Benefit (not leave) should be split in three attendance
benef = self.env['hr.benefit'].create({
@@ -191,8 +171,8 @@ class TestBenefit(TestPayslipBase):
]))
def test_create_benefit_leave(self):
start = self.to_datetime_tz('2015-11-01 09:00:00')
end = self.to_datetime_tz('2015-11-03 18:00:00')
start = datetime(2015, 11, 1, 9, 0, 0)
end = datetime(2015, 11, 3, 18, 0, 0)
benef = self.env['hr.benefit'].create({
'name': 'Richard leave from benef',
@@ -208,8 +188,8 @@ class TestBenefit(TestPayslipBase):
self.assertEqual(calendar_leave.benefit_type_id, benef.benefit_type_id, "It should have the same benefit type")
def test_validate_conflict_benefit(self):
start = self.to_datetime_tz('2015-11-01 09:00:00')
end = self.to_datetime_tz('2015-11-01 13:00:00')
start = datetime(2015, 11, 1, 9, 0, 0)
end = datetime(2015, 11, 1, 13, 0, 0)
benef1 = self.env['hr.benefit'].create({
'name': '1',
'employee_id': self.richard_emp.id,
@@ -261,8 +241,8 @@ class TestBenefit(TestPayslipBase):
self.assertFalse(benef1.action_validate(), "It should not validate benefits without a type")
def test_approve_leave_benefit(self):
start = self.to_datetime_tz('2015-11-01 09:00:00')
end = self.to_datetime_tz('2015-11-03 13:00:00')
start = datetime(2015, 11, 1, 9, 0, 0)
end = datetime(2015, 11, 3, 13, 0, 0)
leave = self.env['hr.leave'].create({
'name': 'Doctor Appointment',
'employee_id': self.richard_emp.id,
@@ -283,13 +263,13 @@ class TestBenefit(TestPayslipBase):
leave.action_approve()
new_leave_benef = self.env['hr.benefit'].search([
('date_start', '=', Datetime.to_datetime('2015-11-01 09:00:00')),
('date_stop', '=', Datetime.to_datetime('2015-11-02 09:00:00')),
('date_start', '=', Datetime.to_datetime(datetime(2015, 11, 1, 9, 0, 0))),
('date_stop', '=', Datetime.to_datetime(datetime(2015, 11, 2, 9, 0, 0))),
('benefit_type_id.is_leave', '=', True)
])
new_benef = self.env['hr.benefit'].search([
('date_start', '=', Datetime.to_datetime('2015-11-02 09:00:01')),
('date_start', '=', Datetime.to_datetime(datetime(2015, 11, 2, 9, 0, 1))),
('date_stop', '=', end),
('benefit_type_id.is_leave', '=', False)
])
@@ -300,8 +280,8 @@ class TestBenefit(TestPayslipBase):
self.assertTrue((new_benef | new_leave_benef).action_validate(), "It should be able to validate the benefits")
def test_refuse_leave_benefit(self):
start = self.to_datetime_tz('2015-11-01 09:00:00')
end = self.to_datetime_tz('2015-11-03 13:00:00')
start = datetime(2015, 11, 1, 9, 0, 0)
end = datetime(2015, 11, 3, 13, 0, 0)
leave = self.env['hr.leave'].create({
'name': 'Doctor Appointment',
'employee_id': self.richard_emp.id,
@@ -330,8 +310,8 @@ class TestBenefit(TestPayslipBase):
self.assertEqual(data['hours'], 168.0)
def test_time_extra_benefit(self):
start = self.to_datetime_tz('2015-11-01 10:00:00')
end = self.to_datetime_tz('2015-11-01 17:00:00')
start = datetime(2015, 11, 1, 10, 0, 0)
end = datetime(2015, 11, 1, 17, 0, 0)
benef = self.env['hr.benefit'].create({
'name': '1',
'employee_id': self.richard_emp.id,
@@ -346,8 +326,8 @@ class TestBenefit(TestPayslipBase):
def test_time_week_leave_benefit(self):
# /!\ this is a week day => it exists an calendar attendance at this time
start = self.to_datetime_tz('2015-11-02 10:00:00', tz=pytz.utc)
end = self.to_datetime_tz('2015-11-02 17:00:00', tz=pytz.utc)
start = datetime(2015, 11, 2, 10, 0, 0)
end = datetime(2015, 11, 2, 17, 0, 0)
leave_benef = self.env['hr.benefit'].create({
'name': '1leave',
'employee_id': self.richard_emp.id,
@@ -362,8 +342,8 @@ class TestBenefit(TestPayslipBase):
def test_time_weekend_leave_benefit(self):
# /!\ this is in the weekend => no calendar attendance at this time
start = self.to_datetime_tz('2015-11-01 10:00:00', tz=pytz.utc)
end = self.to_datetime_tz('2015-11-01 17:00:00', tz=pytz.utc)
start = datetime(2015, 11, 1, 10, 0, 0)
end = datetime(2015, 11, 1, 17, 0, 0)
leave_benef = self.env['hr.benefit'].create({
'name': '1leave',
'employee_id': self.richard_emp.id,
@@ -378,8 +358,8 @@ class TestBenefit(TestPayslipBase):
def test_payslip_generation_with_leave(self):
# /!\ this is a week day => it exists an calendar attendance at this time
start = self.to_datetime_tz('2015-11-02 10:00:00', tz=pytz.utc)
end = self.to_datetime_tz('2015-11-02 17:00:00', tz=pytz.utc)
start = datetime(2015, 11, 2, 10, 0, 0)
end = datetime(2015, 11, 2, 17, 0, 0)
leave_benef = self.env['hr.benefit'].create({
'name': '1leave',
'employee_id': self.richard_emp.id,
@@ -402,8 +382,8 @@ class TestBenefit(TestPayslipBase):
def test_payslip_generation_with_extra_work(self):
# /!\ this is in the weekend (Sunday) => no calendar attendance at this time
start = self.to_datetime_tz('2015-11-01 10:00:00', tz=pytz.utc)
end = self.to_datetime_tz('2015-11-01 17:00:00', tz=pytz.utc)
start = datetime(2015, 11, 1, 10, 0, 0)
end = datetime(2015, 11, 1, 17, 0, 0)
benef = self.env['hr.benefit'].create({
'name': 'Extra',
'employee_id': self.richard_emp.id,
@@ -74,7 +74,6 @@ class TestPayslipMultiContract(TestPayslipContractBase):
start = datetime.strptime('2015-11-05 07:00:00', '%Y-%m-%d %H:%M:%S')
end = datetime.strptime('2015-12-15 18:00:00', '%Y-%m-%d %H:%M:%S')
self.contract_cdi.write({'date_start': datetime.strptime('2015-12-30', '%Y-%m-%d').date()})
print(self.contract_cdi.date_start, 'test')
# begins during contract, ends after contract
leave = self.create_leave(start, end)
leave.action_approve()
+2 -2
View File
@@ -35,8 +35,8 @@
$("head").append($parsedHTML.find(".resources").html());
}
$(".container").html($parsedHTML.find('.pos-customer_facing_display').html());
$(".container").attr('class', 'container').addClass($parsedHTML.find('.pos-customer_facing_display').attr('class'));
$(".container-fluid").html($parsedHTML.find('.pos-customer_facing_display').html());
$(".container-fluid").attr('class', 'container-fluid').addClass($parsedHTML.find('.pos-customer_facing_display').attr('class'));
var d = $('.pos_orderlines_list');
d.scrollTop(d.prop("scrollHeight"));
+1 -1
View File
@@ -39,7 +39,7 @@
</style>
</head>
<body>
<div class="container">
<div class="container-fluid">
<div class="text-center pt-5">
<img style="width: 150px;" src="/web/static/src/img/logo_inverse_white_206px.png">
<p class="mt-3" style="color: #fff;font-size: 30px;">IoTBox</p>
+9 -5
View File
@@ -8,7 +8,7 @@ import io
from odoo import api, fields, models, _
from odoo.exceptions import Warning
from odoo.tools import pycompat
from odoo.tools import float_is_zero, pycompat
class AccountFrFec(models.TransientModel):
@@ -182,14 +182,16 @@ class AccountFrFec(models.TransientModel):
sql_query += '''
GROUP BY aml.account_id, aat.type
HAVING sum(aml.balance) != 0
HAVING round(sum(aml.balance), %s) != 0
AND aat.type not in ('receivable', 'payable')
'''
formatted_date_from = fields.Date.to_string(self.date_from).replace('-', '')
date_from = self.date_from
formatted_date_year = date_from.year
currency_digits = 2
self._cr.execute(
sql_query, (formatted_date_year, formatted_date_from, formatted_date_from, formatted_date_from, self.date_from, company.id))
sql_query, (formatted_date_year, formatted_date_from, formatted_date_from, formatted_date_from, self.date_from, company.id, currency_digits))
for row in self._cr.fetchall():
listrow = list(row)
@@ -202,6 +204,8 @@ class AccountFrFec(models.TransientModel):
current_amount = float(listrow[11].replace(',', '.')) - float(listrow[12].replace(',', '.'))
unaffected_earnings_amount = float(unaffected_earnings_results[11].replace(',', '.')) - float(unaffected_earnings_results[12].replace(',', '.'))
listrow_amount = current_amount + unaffected_earnings_amount
if float_is_zero(listrow_amount, precision_digits=currency_digits):
continue
if listrow_amount > 0:
listrow[11] = str(listrow_amount).replace('.', ',')
listrow[12] = '0,00'
@@ -269,11 +273,11 @@ class AccountFrFec(models.TransientModel):
sql_query += '''
GROUP BY aml.account_id, aat.type, rp.ref, rp.id
HAVING sum(aml.balance) != 0
HAVING round(sum(aml.balance), %s) != 0
AND aat.type in ('receivable', 'payable')
'''
self._cr.execute(
sql_query, (formatted_date_year, formatted_date_from, formatted_date_from, formatted_date_from, self.date_from, company.id))
sql_query, (formatted_date_year, formatted_date_from, formatted_date_from, formatted_date_from, self.date_from, company.id, currency_digits))
for row in self._cr.fetchall():
listrow = list(row)
+2
View File
@@ -208,6 +208,7 @@
<field name="tax_exigibility">on_payment</field>
<field name="cash_basis_account_id" ref="cuenta118_01"/>
<field name="tag_ids" eval="[(6,0,[ref('tag_diot_8')])]"/>
<field name="sequence" eval="10"/>
</record>
<record id="tax17" model="account.tax.template">
@@ -223,6 +224,7 @@
<field name="tax_group_id" ref="tax_group_iva_8"/>
<field name="tax_exigibility">on_payment</field>
<field name="cash_basis_account_id" ref="cuenta208_01"/>
<field name="sequence" eval="10"/>
</record>
</data>
</odoo>
+2 -2
View File
@@ -185,11 +185,11 @@ class LinkTracker(models.Model):
@api.model
def recent_links(self, filter, limit):
if filter == 'newest':
return self.search_read([], order='create_date DESC', limit=limit)
return self.search_read([], order='create_date DESC, id DESC', limit=limit)
elif filter == 'most-clicked':
return self.search_read([('count', '!=', 0)], order='count DESC', limit=limit)
elif filter == 'recently-used':
return self.search_read([('count', '!=', 0)], order='write_date DESC', limit=limit)
return self.search_read([('count', '!=', 0)], order='write_date DESC, id DESC', limit=limit)
else:
return {'Error': "This filter doesn't exist."}
+1 -1
View File
@@ -23,5 +23,5 @@ class IrAttachment(models.Model):
related_record = self.env[self.res_model].browse(self.res_id)
# message_main_attachment_id field can be empty, that's why we compare to False;
# we are just checking that it exists on the model before writing it
if hasattr(related_record, 'message_main_attachment_id'):
if related_record and hasattr(related_record, 'message_main_attachment_id'):
related_record.message_main_attachment_id = self
+2 -1
View File
@@ -197,6 +197,7 @@ class MrpAbstractWorkorder(models.AbstractModel):
move_line.product_uom_qty += self.qty_producing
move_line.qty_done += self.qty_producing
else:
location_dest_id = production_move.location_dest_id.get_putaway_strategy(self.product_id).id or production_move.location_dest_id.id
move_line.create({
'move_id': production_move.id,
'product_id': production_move.product_id.id,
@@ -205,7 +206,7 @@ class MrpAbstractWorkorder(models.AbstractModel):
'product_uom_id': self.product_uom_id.id,
'qty_done': self.qty_producing,
'location_id': production_move.location_id.id,
'location_dest_id': production_move.location_dest_id.id,
'location_dest_id': location_dest_id,
})
else:
rounding = production_move.product_uom.rounding
+111
View File
@@ -7,6 +7,52 @@ from odoo.tests import Form
class TestWarehouse(common.TestMrpCommon):
def setUp(self):
super(TestWarehouse, self).setUp()
self.stock_location = self.env.ref('stock.stock_location_stock')
self.depot_location = self.env['stock.location'].create({
'name': 'Depot',
'usage': 'internal',
'location_id': self.stock_location.id,
})
putaway = self.env['product.putaway'].create({
'name': 'putaway stock->depot',
'fixed_location_ids': [(0, 0, {
'category_id': self.env.ref('product.product_category_all').id,
'fixed_location_id': self.depot_location.id,
})]
})
self.stock_location.write({
'putaway_strategy_id': putaway.id,
})
self.laptop = self.env.ref("product.product_product_25")
graphics_card = self.env.ref("product.product_product_24")
unit = self.env.ref("uom.product_uom_unit")
mrp_routing = self.env.ref("mrp.mrp_routing_0")
self.bom_laptop = self.env['mrp.bom'].create({
'product_tmpl_id': self.laptop.product_tmpl_id.id,
'product_qty': 1,
'product_uom_id': unit.id,
'bom_line_ids': [(0, 0, {
'product_id': graphics_card.id,
'product_qty': 1,
'product_uom_id': unit.id
})],
'routing_id': mrp_routing.id
})
def new_mo_laptop(self):
form = Form(self.env['mrp.production'])
form.product_id = self.laptop
form.product_qty = 1
form.bom_id = self.bom_laptop
p = form.save()
p.action_confirm()
p.action_assign()
return p
def test_manufacturing_route(self):
warehouse_1_stock_manager = self.warehouse_1.sudo(self.user_stock_manager)
@@ -103,6 +149,71 @@ class TestWarehouse(common.TestMrpCommon):
# scrap_move = production_3.move_raw_ids.filtered(lambda x: x.product_id == self.product_2 and x.scrapped)
# self.assertTrue(scrap_move, "There are no any scrap move created for production order.")
def test_putaway_after_manufacturing_1(self):
""" This test checks a manufactured product without tracking will go to
location defined in putaway strategy.
"""
mo_laptop = self.new_mo_laptop()
mo_laptop.button_plan()
workorder = mo_laptop.workorder_ids[0]
workorder.button_start()
workorder.record_production()
mo_laptop.button_mark_done()
# We check if the laptop go in the depot and not in the stock
move = mo_laptop.move_finished_ids
location_dest = move.move_line_ids.location_dest_id
self.assertEqual(location_dest.id, self.depot_location.id)
self.assertNotEqual(location_dest.id, self.stock_location.id)
def test_putaway_after_manufacturing_2(self):
""" This test checks a tracked manufactured product will go to location
defined in putaway strategy.
"""
self.laptop.tracking = 'serial'
mo_laptop = self.new_mo_laptop()
mo_laptop.button_plan()
workorder = mo_laptop.workorder_ids[0]
workorder.button_start()
serial = self.env['stock.production.lot'].create({'product_id': self.laptop.id})
workorder.final_lot_id = serial
workorder.record_production()
mo_laptop.button_mark_done()
# We check if the laptop go in the depot and not in the stock
move = mo_laptop.move_finished_ids
location_dest = move.move_line_ids.location_dest_id
self.assertEqual(location_dest.id, self.depot_location.id)
self.assertNotEqual(location_dest.id, self.stock_location.id)
def test_putaway_after_manufacturing_3(self):
""" This test checks a tracked manufactured product will go to location
defined in putaway strategy when the production is recorded with
product.produce wizard.
"""
self.laptop.tracking = 'serial'
mo_laptop = self.new_mo_laptop()
serial = self.env['stock.production.lot'].create({'product_id': self.laptop.id})
product_produce = self.env['mrp.product.produce'].with_context({
'active_id': mo_laptop.id,
'active_ids': [mo_laptop.id],
}).create({
'product_qty': 1.0,
'lot_id': serial.id,
})
product_produce.do_produce()
mo_laptop.button_mark_done()
# We check if the laptop go in the depot and not in the stock
move = mo_laptop.move_finished_ids
location_dest = move.move_line_ids.location_dest_id
self.assertEqual(location_dest.id, self.depot_location.id)
self.assertNotEqual(location_dest.id, self.stock_location.id)
class TestKitPicking(common.TestMrpCommon):
def setUp(self):
+1 -1
View File
@@ -355,6 +355,7 @@ class PaymentAcquirer(models.Model):
if values is None:
values = {}
values.setdefault('return_url', '/payment/process')
# reference and amount
values.setdefault('reference', reference)
amount = float_round(amount, 2)
@@ -446,7 +447,6 @@ class PaymentAcquirer(models.Model):
'context': self._context,
'type': values.get('type') or 'form',
})
values.setdefault('return_url', False)
_logger.info('payment.acquirer.render: <%s> values rendered for form payment:\n%s', self.provider, pprint.pformat(values))
return self.view_template_id.render(values, engine='ir.qweb')
@@ -81,14 +81,16 @@ odoo.define('payment.processing', function (require) {
'tx_cancel': [],
'tx_error': [],
};
if (transactions.length > 0 && transactions[0].acquirer_provider == 'transfer') {
window.location = transactions[0].return_url;
return;
}
// group the transaction according to their state
transactions.forEach(function (tx) {
var key = 'tx_' + tx.state;
if(key in render_values) {
if (tx.acquirer_provider === 'transfer') {
window.location = tx.return_url;
return;
}
render_values[key].push(tx);
}
});
+1
View File
@@ -895,6 +895,7 @@ class PosOrder(models.Model):
for pack_lot in pack_lots:
lot_id, qty = pack_lot['lot_id'], pack_lot['qty']
self.env['stock.move.line'].create({
'picking_id': move.picking_id.id,
'move_id': move.id,
'product_id': move.product_id.id,
'product_uom_id': move.product_uom.id,
@@ -35,6 +35,7 @@ body .pos-customer_facing_display {
font-weight: 300;
width: 100%;
height: 100%;
padding: 0;
-webkit-display: flex;
-moz-display: flex;
-ms-display: flex;
+2 -1
View File
@@ -731,7 +731,7 @@ td {
border-top-right-radius: 4px;
}
.pos .numpad button {
float: left;
float: left/*rtl:ignore*/; /* rtlcss forced to keep ltr */
height: 54px;
width: 54px;
font-weight: bold;
@@ -2321,6 +2321,7 @@ td {
margin-right: 3px;
}
.pos .popup-numpad {
direction: ltr/*rtl:ignore*/; /* rtlcss forced to keep ltr */
margin: 12px auto;
text-align: center;
width: 254px;
@@ -95,6 +95,7 @@ body {
font-weight: 300;
width: 100%;
height: 100%;
padding: 0;
@include flex-display;
@include flex-direction(row);
+1 -1
View File
@@ -87,7 +87,7 @@ class PortalWizardUser(models.TransientModel):
partners_error_empty |= wizard_user.partner_id
elif email in emails:
partners_error_emails |= wizard_user.partner_id
user = self.env['res.users'].sudo().with_context(active_test=False).search([('login', '=', email)])
user = self.env['res.users'].sudo().with_context(active_test=False).search([('login', '=ilike', email)])
if user:
partners_error_user |= wizard_user.partner_id
emails.append(email)
+26 -6
View File
@@ -308,6 +308,7 @@ class Pricelist(models.Model):
return pricelist.get_products_price(
list(zip(**products_by_qty_by_partner)))
# DEPRECATED (Not used anymore, see d39d583b2) -> Remove me in master (saas12.3)
def _get_partner_pricelist(self, partner_id, company_id=None):
""" Retrieve the applicable pricelist for a given partner in a given company.
@@ -317,34 +318,53 @@ class Pricelist(models.Model):
res = self._get_partner_pricelist_multi([partner_id], company_id)
return res[partner_id].id
def _get_partner_pricelist_multi_search_domain_hook(self):
return []
def _get_partner_pricelist_multi_filter_hook(self):
return self
def _get_partner_pricelist_multi(self, partner_ids, company_id=None):
""" Retrieve the applicable pricelist for given partners in a given company.
It will return the first found pricelist in this order:
First, the pricelist of the specific property (res_id set), this one
is created when saving a pricelist on the partner form view.
Else, it will return the pricelist of the partner country group
Else, it will return the generic property (res_id not set), this one
is created on the company creation.
Else, it will return the first available pricelist
:param company_id: if passed, used for looking up properties,
instead of current user's company
:return: a dict {partner_id: pricelist}
"""
Partner = self.env['res.partner']
# `partner_ids` might be ID from inactive uers. We should use active_test
# as we will do a search() later (real case for website public user).
Partner = self.env['res.partner'].with_context(active_test=False)
Property = self.env['ir.property'].with_context(force_company=company_id or self.env.user.company_id.id)
Pricelist = self.env['product.pricelist']
pl_domain = self._get_partner_pricelist_multi_search_domain_hook()
# retrieve values of property
# if no specific property, try to find a fitting pricelist
result = Property.get_multi('property_product_pricelist', Partner._name, partner_ids)
remaining_partner_ids = [pid for pid, val in result.items() if not val]
remaining_partner_ids = [pid for pid, val in result.items() if not val or
not val._get_partner_pricelist_multi_filter_hook()]
if remaining_partner_ids:
# get fallback pricelist when no pricelist for a given country
pl_fallback = (
Pricelist.search([('country_group_ids', '=', False)], limit=1) or
Pricelist.search(pl_domain + [('country_group_ids', '=', False)], limit=1) or
Property.get('property_product_pricelist', 'res.partner') or
Pricelist.search([], limit=1)
Pricelist.search(pl_domain, limit=1)
)
# group partners by country, and find a pricelist for each country
domain = [('id', 'in', remaining_partner_ids)]
groups = Partner.read_group(domain, ['country_id'], ['country_id'])
for group in groups:
country_id = group['country_id'] and group['country_id'][0]
pl = Pricelist.search([('country_group_ids.country_ids', '=', country_id)], limit=1)
pl = Pricelist.search(pl_domain + [('country_group_ids.country_ids', '=', country_id)], limit=1)
pl = pl or pl_fallback
for pid in Partner.search(group['__domain']).ids:
result[pid] = pl
@@ -238,7 +238,7 @@
<div class="row">
<div t-attf-class="col {{'col-lg-6' if not task.description else 'col-lg-12'}}">
<ul class="list-group">
<a class="list-group-item list-group-item-action d-flex align-items-center oe_attachments py-1 px-2" t-foreach='task.attachment_ids' t-as='attachment' t-attf-href="/web/content/#{attachment.id}" target="_blank">
<a class="list-group-item list-group-item-action d-flex align-items-center oe_attachments py-1 px-2" t-foreach='task.attachment_ids' t-as='attachment' t-attf-href="/web/content/#{attachment.id}" target="_blank" data-no-post-process="">
<div class='oe_attachment_embedded o_image o_image_small mr-2 mr-lg-3' t-att-title="attachment.name" t-att-data-mimetype="attachment.mimetype" t-attf-data-src="/web/image/#{attachment.id}/50x40"/>
<div class='oe_attachment_name text-truncate'><t t-esc='attachment.name'/></div>
</a>
+1 -1
View File
@@ -30,7 +30,7 @@ class PurchaseBillUnion(models.Model):
id as vendor_bill_id, NULL as purchase_order_id
FROM account_invoice
WHERE
type='in_invoice' and state in ('open','in_payment','paid','cancel')
type='in_invoice' and COALESCE(number, '') != ''
UNION
SELECT
-id, name, partner_ref, partner_id, date_order::date as date, amount_untaxed as amount, currency_id, company_id,
+4 -1
View File
@@ -51,7 +51,10 @@ class SaleOrder(models.Model):
def _action_confirm(self):
""" On SO confirmation, some lines should generate a task or a project. """
result = super(SaleOrder, self)._action_confirm()
self.mapped('order_line').sudo().with_context(default_company_id=self.company_id.id)._timesheet_service_generation()
self.mapped('order_line').sudo().with_context(
default_company_id=self.company_id.id,
force_company=self.company_id.id,
)._timesheet_service_generation()
return result
@api.multi
+4 -3
View File
@@ -81,10 +81,11 @@ class MrpStockReport(models.TransientModel):
res_model = ''
ref = ''
res_id = False
if move_line.picking_id:
picking_id = move_line.picking_id or move_line.move_id.picking_id
if picking_id:
res_model = 'stock.picking'
res_id = move_line.picking_id.id
ref = move_line.picking_id.name
res_id = picking_id.id
ref = picking_id.name
elif move_line.move_id.inventory_id:
res_model = 'stock.inventory'
res_id = move_line.move_id.inventory_id.id
+1 -1
View File
@@ -173,7 +173,7 @@ var QWeb2 = {
new_dict[as_first] = index === 0;
new_dict[as_last] = index + 1 === size;
new_dict[as_parity] = (index % 2 == 1 ? 'odd' : 'even');
if (cur.constructor === Object) {
if (cur && cur.constructor === Object) {
this.extend(new_dict, cur);
}
new_dict[as] = cur;
@@ -370,7 +370,7 @@ var FormRenderer = BasicRenderer.extend({
var visible_buttons = buttons_partition[1];
// Get the unfolded buttons according to window size
var nb_buttons = [2, 2, 4, 6][config.device.size_class] || 7;
var nb_buttons = this._renderButtonBoxNbButtons();
var unfolded_buttons = visible_buttons.slice(0, nb_buttons).concat(invisible_buttons);
// Get the folded buttons
@@ -410,6 +410,13 @@ var FormRenderer = BasicRenderer.extend({
this._registerModifiers(node, this.state, $result);
return $result;
},
/**
* @private
* @returns {integer}
*/
_renderButtonBoxNbButtons: function () {
return [2, 2, 4, 6][config.device.size_class] || 7;
},
/**
* @private
* @param {Object} node
@@ -440,12 +440,10 @@ var PivotModel = AbstractModel.extend({
// if we don't have the key 'groupBy' in params. In that case,
// we want to have the full open state for the groupbys.
self._updateTree(old_row.root, self.data.main_row.root);
self._updateMainGroupBys(old_row, self.data.main_row);
}
if (!('pivot_column_groupby' in (params.context || {}))) {
self._updateTree(old_col.root, self.data.main_col.root);
self._updateMainGroupBys(old_col, self.data.main_col);
}
});
},
@@ -986,7 +984,8 @@ var PivotModel = AbstractModel.extend({
});
var index = 0;
var rowGroupBys = !_.isEmpty(this.data.groupedBy) ? this.data.groupedBy : this.initialRowGroupBys;
var rowGroupBys = !_.isEmpty(this.data.groupedBy) ? this.data.groupedBy : this.initialRowGroupBys.slice();
this.data.groupedBy = rowGroupBys;
var colGroupBys = this.data.colGroupBys;
var dataPoint, row, col, attrs, cell_value;
var main_row_header, main_col_header;
@@ -1118,19 +1117,6 @@ var PivotModel = AbstractModel.extend({
this._traverseTree(root.children[i], f, arg1, arg2, arg3);
}
},
/**
* Updates the groupBys that the main[row | col] holds
*
* @private
* @param {Object} old: The main abstract header before reload
* @param {Object} main: The main abstract header after reload
*
*/
_updateMainGroupBys: function (old, main) {
var new_groupby_length = this._getHeaderDepth(main.root) - 1;
var new_groupby_list = old.root.groupbys.slice(0, new_groupby_length);
main.root.groupbys = new_groupby_list;
},
/**
* @param {Object} old_tree
* @param {Object} new_tree
+35 -3
View File
@@ -1096,7 +1096,7 @@ QUnit.module('Views', {
pivot.destroy();
});
QUnit.skip('Reload, group by columns, reload', function (assert) {
QUnit.test('Reload, group by columns, reload', function (assert) {
assert.expect(2);
var pivot = createView({
@@ -1118,7 +1118,7 @@ QUnit.module('Views', {
pivot_row_groupby: []};
// Check that column groupbys were not lost
assert.deepEqual(pivot.getContext(), expectedContext,
assert.deepEqual(pivot.getOwnedQueryParams(), {context: expectedContext},
'Column groupby not lost after first reload');
// Set a column groupby
@@ -1132,12 +1132,44 @@ QUnit.module('Views', {
pivot_measures: ['__count'],
pivot_row_groupby: []};
assert.deepEqual(pivot.getContext(), expectedContext,
assert.deepEqual(pivot.getOwnedQueryParams(), {context: expectedContext},
'Column groupby not lost after second reload');
pivot.destroy();
});
QUnit.test('Empty results keep groupbys', function (assert) {
assert.expect(2);
var pivot = createView({
View: PivotView,
model: "partner",
data: this.data,
arch: '<pivot/>',
});
// Set a column groupby
pivot.$('thead .o_pivot_header_cell_closed').click();
pivot.$('.o_field_selection .dropdown-item[data-field=customer]').click();
// Set a domain for empty results
pivot.update({domain: [['id', '=', false]]});
var expectedContext = {pivot_column_groupby: ['customer'],
pivot_measures: ['__count'],
pivot_row_groupby: []};
assert.deepEqual(pivot.getOwnedQueryParams(), {context: expectedContext},
'Column groupby not lost after empty results');
// Set a domain for not empty results
pivot.update({domain: [['product_id', '=', 37]]});
assert.deepEqual(pivot.getOwnedQueryParams(), {context: expectedContext},
'Column groupby not lost after reload after empty results');
pivot.destroy();
});
QUnit.test('correctly uses pivot_ keys from the context', function (assert) {
assert.expect(7);
@@ -132,7 +132,7 @@ var SnippetOption = Widget.extend({
* @param {jQuery} $opt - the related DOMElement option
*/
selectClass: function (previewMode, value, $opt) {
var $group = $opt && $opt.closest('.dropdown-submenu');
var $group = $opt && $opt.parents('.dropdown-submenu').last();
if (!$group || !$group.length) {
$group = this.$el;
}
@@ -295,13 +295,29 @@ var SnippetOption = Widget.extend({
})
.addClass('active');
_processSelectClassElements(this.$el);
// Get submenus which are not inside submenus
var $submenus = this.$el.find('.dropdown-submenu')
.addBack('.dropdown-submenu')
.not('.dropdown-submenu .dropdown-submenu');
function _processSelectClassElements($el) {
// Add unique active class for each submenu active item
_.each($submenus, function (submenu) {
var $elements = _getSelectClassElements($(submenu));
_processSelectClassElements($elements);
});
// Add unique active class for out-of-submenu active item
var $externalElements = _getSelectClassElements(this.$el)
.not('.dropdown-submenu *, .dropdown-submenu');
_processSelectClassElements($externalElements);
function _getSelectClassElements($el) {
return $el.find('[data-select-class]')
.addBack('[data-select-class]');
}
function _processSelectClassElements($elements) {
var maxNbClasses = -1;
$el.find('[data-select-class]')
.addBack('[data-select-class]')
.removeClass('active')
$elements.removeClass('active')
.filter(function () {
var className = $(this).data('selectClass');
var nbClasses = className ? className.split(' ').length : 0;
+17
View File
@@ -42,6 +42,23 @@ def sitemap_qs2dom(qs, route, field='name'):
return dom
def get_request_website():
""" Return the website set on `request` if called in a frontend context
(website=True on route).
This method can typically be used to check if we are in the frontend.
This method is easy to mock during python tests to simulate frontend
context, rather than mocking every method accessing request.website.
Don't import directly the method or it won't be mocked during tests, do:
```
from odoo.addons.website.models import ir_http
my_var = ir_http.get_request_website()
```
"""
return request and getattr(request, 'website', False) or False
class Http(models.AbstractModel):
_inherit = 'ir.http'
+9 -1
View File
@@ -1,5 +1,6 @@
# coding: utf-8
from odoo import api, models
from odoo.addons.website.models import ir_http
class IrRule(models.Model):
@@ -8,7 +9,14 @@ class IrRule(models.Model):
@api.model
def _eval_context(self):
res = super(IrRule, self)._eval_context()
res['website_id'] = self.env['website'].get_current_website().id
# We need is_frontend to avoid showing website's company items in backend
# (that could be different than current company). We can't use
# `get_current_website(falback=False)` as it could also return a website
# in backend (if domain set & match)..
is_frontend = ir_http.get_request_website()
Website = self.env['website']
res['website'] = is_frontend and Website.get_current_website() or Website
return res
def _compute_domain_keys(self):
@@ -642,11 +642,11 @@ var FacebookPageDialog = weWidgets.Dialog.extend({
*/
_renderPreview: function () {
var self = this;
var match = this.fbData.href.match(/^(?:https?:\/\/)?(?:www\.)?(?:fb|facebook)\.com\/(\w+)/);
var match = this.fbData.href.match(/^(?:https?:\/\/)?(?:www\.)?(?:fb|facebook)\.com\/(?:([\w.]+)|[^/?#]+-([0-9]{15,16}))(?:$|[\/?# ])/);
if (match) {
// Check if the page exists on Facebook or not
$.ajax({
url: 'https://graph.facebook.com/' + match[1] + '/picture',
url: 'https://graph.facebook.com/' + (match[2] || match[1]) + '/picture',
statusCode: {
200: function () {
self._toggleWarning(true);
+1
View File
@@ -13,6 +13,7 @@
<field name="type">ir.actions.act_window</field>
<field name="res_model">ir.module.module</field>
<field name="view_mode">kanban,tree,form</field>
<field name="domain">['!', ('name', '=like', 'theme_%')]</field>
<field name="context" eval="{'search_default_category_id': ref('base.module_category_website')}"/>
</record>
@@ -98,7 +98,7 @@ sAnimations.registry.websiteBlog = sAnimations.Class.extend({
url = 'https://plus.google.com/share?url=' + window.location.href;
}
} else {
var blogPost = $element.parents('[name="blogPost"]');
var blogPost = $element.parents('[name="blog_post"]');
var blogPostTitle = blogPost.find('.o_blog_post_title').html() || '';
var blogArticleLink = blogPost.find('.o_blog_post_title').parent('a').attr('href');
if ($element.hasClass('o_twitter')) {
@@ -207,7 +207,7 @@
<template id="opt_blog_post_grid_layout" name="Grid view"
customize_show="True" active="False" inherit_id="website_blog.blog_post_short">
<xpath expr="//div[@id='main_column']" position="attributes">
<attribute name="class" separator=" " add="d-flex" />
<attribute name="class" separator=" " add="d-flex flex-wrap" />
</xpath>
<xpath expr="//div[@name='blog_post']" position="attributes">
<attribute name="class" add="col-md-5 o_grid" separator=" "/>
+4 -1
View File
@@ -181,7 +181,10 @@ class WebsiteEventController(http.Controller):
def event_register(self, event, **post):
if not event.can_access_from_current_website():
raise werkzeug.exceptions.NotFound()
if not request.context.get('pricelist'):
pricelist = request.website.get_current_pricelist()
if pricelist:
event = event.with_context(pricelist=pricelist.id)
values = {
'event': event,
'main_object': event,
@@ -52,9 +52,13 @@ tour.register('website_links_tour', {
},
},
// 3. Check that counter got incremented and charts are correctly displayed
{
content: "filter recently used links",
trigger: '#filter-recently-used-links',
},
{
content: "visit link stats page",
trigger: '#o_website_links_recent_links a:containsExact("Stats"):first()',
trigger: '#o_website_links_recent_links a:containsExact("Stats"):first',
},
{
content: "check click number and ensure graphs are initialized",
@@ -183,6 +183,7 @@ sAnimations.registry.websiteLinksCharts = sAnimations.Class.extend({
$('#all_time_charts').prepend(_t("There is no data to show"));
$('#last_month_charts').prepend(_t("There is no data to show"));
$('#last_week_charts').prepend(_t("There is no data to show"));
return;
}
var formattedClicksByDay = {};
-2
View File
@@ -1,13 +1,11 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import odoo.tests
import unittest
@odoo.tests.common.tagged('post_install', '-at_install')
class TestUi(odoo.tests.HttpCase):
@unittest.skip("Race condition on runbot or on local with Docker. Merging the test skipped for history purpose as the tour has been reviewed.")
def test_01_test_ui(self):
self.env['link.tracker'].create({
'campaign_id': 2,
@@ -87,7 +87,7 @@
</div>
<h2 class="o_page_header">Your tracked links
<small class="float-right d-none d-md-block mt16" id="filters">
<small class="float-right d-none d-md-block" id="filters">
<ul class="nav nav-tabs nav-tabs-inline graph-tabs" role="tablist">
<li class="nav-item"><a aria-controls="filter-newest-links" href="#" class="nav-link active" id="filter-newest-links" role="tab" data-toggle="tab">Newest</a></li>
<li class="nav-item"><a aria-controls="filter-most-clicked-links" href="#" class="nav-link" id="filter-most-clicked-links" role="tab" data-toggle="tab">Most Clicked</a></li>
+13 -1
View File
@@ -1,7 +1,19 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, SUPERUSER_ID
from . import controllers
from . import models
from . import wizard
from . import report
def uninstall_hook(cr, registry):
''' Need to reenable the `product` pricelist multi-company rule that were
disabled to be 'overriden' for multi-website purpose
'''
env = api.Environment(cr, SUPERUSER_ID, {})
pl_rule = env.ref('product.product_pricelist_comp_rule', raise_if_not_found=False)
pl_item_rule = env.ref('product.product_pricelist_item_comp_rule', raise_if_not_found=False)
multi_company_rules = pl_rule or env['ir.rule']
multi_company_rules += pl_item_rule or env['ir.rule']
multi_company_rules.write({'active': True})
+1
View File
@@ -30,4 +30,5 @@
'qweb': ['static/src/xml/*.xml'],
'installable': True,
'application': True,
'uninstall_hook': 'uninstall_hook',
}
+1 -1
View File
@@ -37,7 +37,7 @@
<record model="product.pricelist" id="product.list0">
<field name="selectable" eval="False" />
<field name="website_id" ref="website.default_website" />
<field name="website_id" eval="False"/>
</record>
</data>
<data>
-1
View File
@@ -224,7 +224,6 @@
<record model="product.pricelist" id="product.list0">
<field name="selectable" eval="True" />
<field name="website_id" ref="website.default_website" />
<field name="sequence">3</field>
<field name="currency_id" ref="base.USD"/>
</record>
+57 -3
View File
@@ -3,6 +3,7 @@
from odoo import api, fields, models, tools, _
from odoo.addons import decimal_precision as dp
from odoo.addons.website.models import ir_http
from odoo.tools.translate import html_translate
from odoo.osv import expression
@@ -19,14 +20,17 @@ class ProductPricelist(models.Model):
_inherit = "product.pricelist"
def _default_website(self):
return self.env['website'].search([], limit=1)
""" Find the first company's website, if there is one. """
company_id = self.env.user.company_id.id
domain = [('company_id', '=', company_id)]
return self.env['website'].search(domain, limit=1)
website_id = fields.Many2one('website', string="website", default=_default_website)
website_id = fields.Many2one('website', string="Website", default=_default_website)
code = fields.Char(string='E-commerce Promotional Code', groups="base.group_user")
selectable = fields.Boolean(help="Allow the end user to choose this price list")
def clear_cache(self):
# website._get_pl() is cached to avoid to recompute at each request the
# website._get_pl_partner_order() is cached to avoid to recompute at each request the
# list of available pricelists. So, we need to invalidate the cache when
# we change the config of website price list to force to recompute.
website = self.env['website']
@@ -50,6 +54,56 @@ class ProductPricelist(models.Model):
self.clear_cache()
return res
def _get_partner_pricelist_multi_search_domain_hook(self):
domain = super(ProductPricelist, self)._get_partner_pricelist_multi_search_domain_hook()
website = ir_http.get_request_website()
if website:
domain += self._get_website_pricelists_domain(website.id)
return domain
def _get_partner_pricelist_multi_filter_hook(self):
res = super(ProductPricelist, self)._get_partner_pricelist_multi_filter_hook()
website = ir_http.get_request_website()
if website:
res = res.filtered(lambda pl: pl._is_available_on_website(website.id))
return res
@api.multi
def _is_available_on_website(self, website_id):
""" To be able to be used on a website, a pricelist should either:
- Have its `website_id` set to current website (specific pricelist).
- Have no `website_id` set and should be `selectable` (generic pricelist)
or should have a `code` (generic promotion).
Note: A pricelist without a website_id, not selectable and without a
code is a backend pricelist.
Change in this method should be reflected in `_get_website_pricelists_domain`.
"""
self.ensure_one()
return self.website_id.id == website_id or (not self.website_id and (self.selectable or self.code))
def _get_website_pricelists_domain(self, website_id):
''' Check above `_is_available_on_website` for explanation.
Change in this method should be reflected in `_is_available_on_website`.
'''
return [
'|', ('website_id', '=', website_id),
'&', ('website_id', '=', False),
'|', ('selectable', '=', True), ('code', '!=', False),
]
def _get_partner_pricelist_multi(self, partner_ids, company_id=None):
''' If `property_product_pricelist` is read from website, we should use
the website's company and not the user's one.
Passing a `company_id` to super will avoid using the current user's
company.
'''
website = ir_http.get_request_website()
if not company_id and website:
company_id = website.company_id.id
return super(ProductPricelist, self)._get_partner_pricelist_multi(partner_ids, company_id)
class ProductPublicCategory(models.Model):
_name = "product.public.category"
+4 -3
View File
@@ -2,7 +2,7 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, fields, models
from odoo.http import request
from odoo.addons.website.models import ir_http
class ResPartner(models.Model):
@@ -16,10 +16,11 @@ class ResPartner(models.Model):
for partner in self:
is_public = any([u._is_public()
for u in partner.with_context(active_test=False).user_ids])
if request and hasattr(request, 'website') and not is_public:
website = ir_http.get_request_website()
if website and not is_public:
partner.last_website_so_id = SaleOrder.search([
('partner_id', '=', partner.id),
('website_id', '=', request.website.id),
('website_id', '=', website.id),
('state', '=', 'draft'),
], order='write_date desc', limit=1)
else:
+45 -19
View File
@@ -6,6 +6,7 @@ import logging
from odoo import api, fields, models, tools
from odoo.http import request
from odoo.addons.website.models import ir_http
_logger = logging.getLogger(__name__)
@@ -41,7 +42,12 @@ class Website(models.Model):
@api.one
def _compute_pricelist_ids(self):
self.pricelist_ids = self.env["product.pricelist"].search([("website_id", "=", self.id)])
""" Return the pricelists that can be used directly or indirectly on
the website.
"""
Pricelist = self.env["product.pricelist"]
domain = Pricelist._get_website_pricelists_domain(self.id)
self.pricelist_ids = Pricelist.search(domain)
@api.multi
def _compute_pricelist_id(self):
@@ -64,57 +70,77 @@ class Website(models.Model):
:param int order_pl: the current cart pricelist
:returns: list of pricelist ids
"""
def _check_show_visible(pl):
""" If `show_visible` is True, we will only show the pricelist if
one of this condition is met:
- The pricelist is `selectable`.
- The pricelist is either the currently used pricelist or the
current cart pricelist, we should consider it as available even if
it might not be website compliant (eg: it is not selectable anymore,
it is a backend pricelist, it is not active anymore..).
"""
return (not show_visible or pl.selectable or pl.id in (current_pl, order_pl))
# Note: 1. pricelists from all_pl are already website compliant (went through
# `_get_website_pricelists_domain`)
# 2. do not read `property_product_pricelist` here as `_get_pl_partner_order`
# is cached and the result of this method will be impacted by that field value.
# Pass it through `partner_pl` parameter instead to invalidate the cache.
# If there is a GeoIP country, find a pricelist for it
self.ensure_one()
pricelists = self.env['product.pricelist']
if country_code:
for cgroup in self.env['res.country.group'].search([('country_ids.code', '=', country_code)]):
for group_pricelists in cgroup.pricelist_ids:
if not show_visible or group_pricelists.selectable or group_pricelists.id in (current_pl, order_pl):
pricelists |= group_pricelists
pricelists |= cgroup.pricelist_ids.filtered(
lambda pl: pl._is_available_on_website(self.id) and _check_show_visible(pl)
)
partner = self.env.user.partner_id
# no GeoIP or no pricelist for this country
if not country_code or not pricelists:
pricelists |= all_pl.filtered(lambda pl: _check_show_visible(pl))
# if logged in, add partner pl (which is `property_product_pricelist`, might not be website compliant)
is_public = self.user_id.id == self.env.user.id
if not is_public and (not pricelists or (partner_pl or partner.property_product_pricelist.id) != website_pl):
if partner.property_product_pricelist.website_id:
pricelists |= partner.property_product_pricelist
if not pricelists: # no pricelist for this country, or no GeoIP
pricelists |= all_pl.filtered(lambda pl: not show_visible or pl.selectable or pl.id in (current_pl, order_pl))
if not show_visible and not country_code:
pricelists |= all_pl.filtered(lambda pl: pl.sudo().code)
if not is_public:
pricelists |= pricelists.browse(partner_pl).filtered(lambda pl: pl._is_available_on_website(self.id) and _check_show_visible(pl))
# This method is cached, must not return records! See also #8795
return pricelists.ids
# DEPRECATED (Not used anymore) -> Remove me in master (saas12.3)
def _get_pl(self, country_code, show_visible, website_pl, current_pl, all_pl):
pl_ids = self._get_pl_partner_order(country_code, show_visible, website_pl, current_pl, all_pl)
return self.env['product.pricelist'].browse(pl_ids)
def get_pricelist_available(self, show_visible=False):
def _get_pricelist_available(self, req, show_visible=False):
""" Return the list of pricelists that can be used on website for the current user.
Country restrictions will be detected with GeoIP (if installed).
:param bool show_visible: if True, we don't display pricelist where selectable is False (Eg: Code promo)
:returns: pricelist recordset
"""
website = request and hasattr(request, 'website') and request.website or None
website = ir_http.get_request_website()
if not website:
if self.env.context.get('website_id'):
website = self.browse(self.env.context['website_id'])
else:
# In the weird case we are coming from the backend (https://github.com/odoo/odoo/issues/20245)
website = len(self) == 1 and self or self.search([], limit=1)
isocountry = request and request.session.geoip and request.session.geoip.get('country_code') or False
isocountry = req and req.session.geoip and req.session.geoip.get('country_code') or False
partner = self.env.user.partner_id
last_order_pl = partner.last_website_so_id.pricelist_id
partner_pl = partner.property_product_pricelist
partner_pl = partner.sudo(user=self.env.user).property_product_pricelist
pricelists = website._get_pl_partner_order(isocountry, show_visible,
website.user_id.sudo().partner_id.property_product_pricelist.id,
request and request.session.get('website_sale_current_pl') or None,
req and req.session.get('website_sale_current_pl') or None,
website.pricelist_ids,
partner_pl=partner_pl and partner_pl.id or None,
order_pl=last_order_pl and last_order_pl.id or None)
return self.env['product.pricelist'].browse(pricelists)
def get_pricelist_available(self, show_visible=False):
return self._get_pricelist_available(request, show_visible)
def is_pricelist_available(self, pl_id):
""" Return a boolean to specify if a specific pricelist can be manually set on the website.
Warning: It check only if pricelist is in the 'selectable' pricelists or the current pricelist.
+26 -1
View File
@@ -52,6 +52,31 @@
<field name="implied_ids" eval="[(4, ref('sale.group_delivery_invoice_address'))]"/>
</record>
<!--
Multi-company/Multi-website compliant:
We can't add a condition on domain_force without losing `product`
ir.rule domain_force. It is better to disabled them to be able to
reenable them on `website_sale` uninstall.
Don't override domain_force or we will need to hardcode the original
domain in `uninstall_hook` rather than just reenabling records.
-->
<record id="product.product_pricelist_comp_rule" model="ir.rule">
<field name="active" eval="False"/>
</record>
<record id="product.product_pricelist_item_comp_rule" model="ir.rule">
<field name="active" eval="False"/>
</record>
<record id="product_pricelist_comp_rule" model="ir.rule">
<field name="name">product pricelist company rule</field>
<field name="model_id" ref="product.model_product_pricelist"/>
<field name="global" eval="True"/>
<field name="domain_force">[('company_id','in',(False,user.company_id.id,website.company_id.id))]</field>
</record>
<record id="product_pricelist_item_comp_rule" model="ir.rule">
<field name="name">product pricelist item company rule</field>
<field name="model_id" ref="product.model_product_pricelist_item"/>
<field name="global" eval="True"/>
<field name="domain_force">[('company_id','in',(False,user.company_id.id,website.company_id.id))]</field>
</record>
</odoo>
@@ -4,7 +4,25 @@ try:
from unittest.mock import patch
except ImportError:
from mock import patch
from odoo.tests.common import TransactionCase
from odoo.tests.common import HttpCase, TransactionCase
''' /!\/!\
Calling `get_pricelist_available` after setting `property_product_pricelist` on
a partner will not work as expected. That field will change the output of
`get_pricelist_available` but modifying it will not invalidate the cache.
Thus, tests should not do:
self.env.user.partner_id.property_product_pricelist = my_pricelist
pls = self.get_pricelist_available()
self.assertEqual(...)
self.env.user.partner_id.property_product_pricelist = another_pricelist
pls = self.get_pricelist_available()
self.assertEqual(...)
as `_get_pl_partner_order` cache won't be invalidate between the calls, output
won't be the one expected and tests will actually not test anything.
Try to keep one call to `get_pricelist_available` by test method.
'''
class TestWebsitePriceList(TransactionCase):
@@ -47,14 +65,14 @@ class TestWebsitePriceList(TransactionCase):
self.addCleanup(patcher.stop)
def get_pl(self, show, current_pl, country):
pls = self.website._get_pl(
pl_ids = self.website._get_pl_partner_order(
country,
show,
self.website.pricelist_id.id,
current_pl,
self.website.pricelist_ids
)
return pls
return self.env['product.pricelist'].browse(pl_ids)
def test_get_pricelist_available_show(self):
show = True
@@ -125,3 +143,291 @@ class TestWebsitePriceList(TransactionCase):
pls = self.get_pl(show, current_pl, country)
self.assertEquals(len(set(pls.mapped('name')) & set(result)), len(pls), 'Test failed for %s (%s %s vs %s %s)'
% (country, len(pls), pls.mapped('name'), len(result), result))
def simulate_frontend_context(self, website_id=1):
# Mock this method will be enough to simulate frontend context in most methods
def get_request_website():
return self.env['website'].browse(website_id)
patcher = patch('odoo.addons.website.models.ir_http.get_request_website', wraps=get_request_website)
patcher.start()
self.addCleanup(patcher.stop)
class DotDict(dict):
"""dot.notation access to dictionary attributes"""
def __getattr__(*args):
val = dict.get(*args)
return DotDict(val) if type(val) is dict else val
class TestWebsitePriceListAvailable(TransactionCase):
# This is enough to avoid a mock (request.session/website do not exist during test)
def get_pricelist_available(self, show_visible=False, website_id=1, country_code=None, website_sale_current_pl=None):
request = DotDict({
'website': self.env['website'].browse(website_id),
'session': {
'geoip': {
'country_code': country_code,
},
'website_sale_current_pl': website_sale_current_pl,
},
})
return self.env['website']._get_pricelist_available(request, show_visible)
def setUp(self):
super(TestWebsitePriceListAvailable, self).setUp()
Pricelist = self.env['product.pricelist']
Website = self.env['website']
# Set up 2 websites
self.website = Website.browse(1)
self.website2 = Website.create({'name': 'Website 2'})
# Remove existing pricelists and create new ones
Pricelist.search([]).write({'active': False})
self.backend_pl = Pricelist.create({
'name': 'Backend Pricelist',
'website_id': False,
})
self.generic_pl_select = Pricelist.create({
'name': 'Generic Selectable Pricelist',
'selectable': True,
'website_id': False,
})
self.generic_pl_code = Pricelist.create({
'name': 'Generic Code Pricelist',
'code': 'GENERICCODE',
'website_id': False,
})
self.generic_pl_code_select = Pricelist.create({
'name': 'Generic Code Selectable Pricelist',
'code': 'GENERICCODESELECT',
'selectable': True,
'website_id': False,
})
self.w1_pl = Pricelist.create({
'name': 'Website 1 Pricelist',
'website_id': self.website.id,
})
self.w1_pl_select = Pricelist.create({
'name': 'Website 1 Pricelist Selectable',
'website_id': self.website.id,
'selectable': True,
})
self.w1_pl_code = Pricelist.create({
'name': 'Website 1 Pricelist Code',
'website_id': self.website.id,
'code': 'W1CODE',
})
self.w1_pl_code_select = Pricelist.create({
'name': 'Website 1 Pricelist Code Selectable',
'website_id': self.website.id,
'code': 'W1CODESELECT',
'selectable': True,
})
self.w2_pl = Pricelist.create({
'name': 'Website 2 Pricelist',
'website_id': self.website2.id,
})
simulate_frontend_context(self)
def test_get_pricelist_available(self):
# all_pl = self.backend_pl + self.generic_pl_select + self.generic_pl_code + self.generic_pl_code_select + self.w1_pl + self.w1_pl_select + self.w1_pl_code + self.w1_pl_code_select + self.w2_pl
# Test get all available pricelists
pls_to_return = self.generic_pl_select + self.generic_pl_code + self.generic_pl_code_select + self.w1_pl + self.w1_pl_select + self.w1_pl_code + self.w1_pl_code_select
pls = self.get_pricelist_available()
self.assertEqual(pls, pls_to_return, "Every pricelist having the correct website_id set or (no website_id but a code or selectable) should be returned")
# Test get all available and visible pricelists
pls_to_return = self.generic_pl_select + self.generic_pl_code_select + self.w1_pl_select + self.w1_pl_code_select
pls = self.get_pricelist_available(show_visible=True)
self.assertEqual(pls, pls_to_return, "Only selectable pricelists website compliant (website_id False or current website) should be returned")
def test_property_product_pricelist_for_inactive_partner(self):
# `_get_partner_pricelist_multi` should consider inactive users when searching for pricelists.
# Real case if for public user. His `property_product_pricelist` need to be set as it is passed
# through `_get_pl_partner_order` as the `website_pl` when searching for available pricelists
# for active users.
public_partner = self.env.ref('base.public_partner')
self.assertFalse(public_partner.active, "Ensure public partner is inactive (purpose of this test)")
pl = public_partner.property_product_pricelist
self.assertEqual(len(pl), 1, "Inactive partner should still get a `property_product_pricelist`")
class TestWebsitePriceListAvailableGeoIP(TestWebsitePriceListAvailable):
def setUp(self):
super(TestWebsitePriceListAvailableGeoIP, self).setUp()
# clean `property_product_pricelist` for partner for this test (clean setup)
self.env['ir.property'].search([('res_id', '=', 'res.partner,%s' % self.env.user.partner_id.id)]).unlink()
# set different country groups on pricelists
c_EUR = self.env.ref('base.europe')
c_BENELUX = self.env.ref('website_sale.benelux')
self.BE = self.env.ref('base.be')
NL = self.env.ref('base.nl')
c_BE = self.env['res.country.group'].create({'name': 'Belgium', 'country_ids': [(6, 0, [self.BE.id])]})
c_NL = self.env['res.country.group'].create({'name': 'Netherlands', 'country_ids': [(6, 0, [NL.id])]})
(self.backend_pl + self.generic_pl_select + self.generic_pl_code + self.w1_pl_select).write({'country_group_ids': [(6, 0, [c_BE.id])]})
(self.generic_pl_code_select + self.w1_pl + self.w2_pl).write({'country_group_ids': [(6, 0, [c_BENELUX.id])]})
(self.w1_pl_code).write({'country_group_ids': [(6, 0, [c_EUR.id])]})
(self.w1_pl_code_select).write({'country_group_ids': [(6, 0, [c_NL.id])]})
# pricelist | selectable | website | code | country group |
# ----------------------------------------------------------------------|
# backend_pl | | | | BE |
# generic_pl_select | V | | | BE |
# generic_pl_code | | | V | BE |
# generic_pl_code_select | V | | V | BENELUX |
# w1_pl | | 1 | | BENELUX |
# w1_pl_select | V | 1 | | BE |
# w1_pl_code | | 1 | V | EUR |
# w1_pl_code_select | V | 1 | V | NL |
# w2_pl | | 2 | | BENELUX |
# available pl for website 1 for GeoIP BE (anything except website 2, backend and NL)
self.website1_be_pl = self.generic_pl_select + self.generic_pl_code + self.w1_pl_select + self.generic_pl_code_select + self.w1_pl + self.w1_pl_code
def test_get_pricelist_available_geoip(self):
# Test get all available pricelists with geoip and no partner pricelist (ir.property)
# property_product_pricelist will also be returned in the available pricelists
self.website1_be_pl += self.env.user.partner_id.property_product_pricelist
pls = self.get_pricelist_available(country_code=self.BE.code)
self.assertEqual(pls, self.website1_be_pl, "Only pricelists for BE and accessible on website should be returned, and the partner pl")
def test_get_pricelist_available_geoip2(self):
# Test get all available pricelists with geoip and a partner pricelist (ir.property) not website compliant
self.env.user.partner_id.property_product_pricelist = self.backend_pl
pls = self.get_pricelist_available(country_code=self.BE.code)
self.assertEqual(pls, self.website1_be_pl, "Only pricelists for BE and accessible on website should be returned as partner pl is not website compliant")
def test_get_pricelist_available_geoip3(self):
# Test get all available pricelists with geoip and a partner pricelist (ir.property) website compliant (but not geoip compliant)
self.env.user.partner_id.property_product_pricelist = self.w1_pl_code_select
self.website1_be_pl += self.env.user.partner_id.property_product_pricelist
pls = self.get_pricelist_available(country_code=self.BE.code)
self.assertEqual(pls, self.website1_be_pl, "Only pricelists for BE and accessible on website should be returned, plus the partner pricelist as it is website compliant")
def test_get_pricelist_available_geoip4(self):
# Test get all available with geoip and visible pricelists + promo pl
pls_to_return = self.generic_pl_select + self.w1_pl_select + self.generic_pl_code_select
# property_product_pricelist will also be returned in the available pricelists
pls_to_return += self.env.user.partner_id.property_product_pricelist
current_pl = self.w1_pl_code
pls = self.get_pricelist_available(country_code=self.BE.code, show_visible=True, website_sale_current_pl=current_pl.id)
self.assertEqual(pls, pls_to_return + current_pl, "Only pricelists for BE, accessible en website and selectable should be returned. It should also return the applied promo pl")
class TestWebsitePriceListHttp(HttpCase):
def test_get_pricelist_available_multi_company(self):
''' Test that the `property_product_pricelist` of `res.partner` is not
computed as SUPERUSER_ID.
Indeed, `property_product_pricelist` is a _compute that ends up
doing a search on `product.pricelist` that woule bypass the
pricelist multi-company `ir.rule`. Then it would return pricelists
from another company and the code would raise an access error when
reading that `property_product_pricelist`.
'''
test_company = self.env['res.company'].create({'name': 'Test Company'})
self.env['product.pricelist'].create({
'name': 'Backend Pricelist For "Test Company"',
'website_id': False,
'company_id': test_company.id,
'sequence': 1,
})
self.authenticate('portal', 'portal')
r = self.url_open('/shop')
self.assertEqual(r.status_code, 200, "The page should not raise an access error because of reading pricelists from other companies")
class TestWebsitePriceListMultiCompany(TransactionCase):
def setUp(self):
''' Create a basic multi-company pricelist environment:
- Set up 2 companies with their own company-restricted pricelist each.
- Add demo user in those 2 companies
- For each company, add that company pricelist to the demo user partner.
- Set website's company to company 2
- Demo user will still be in company 1
'''
super(TestWebsitePriceListMultiCompany, self).setUp()
self.demo_user = self.env.ref('base.user_demo')
# Create and add demo user to 2 companies
self.company1 = self.demo_user.company_id
self.company2 = self.env['res.company'].create({'name': 'Test Company'})
self.demo_user.company_ids += self.company2
# Set company2 as current company for demo user
self.website = self.env['website'].browse(1)
self.website.company_id = self.company2
# Create a company pricelist for each company and set it to demo user
self.c1_pl = self.env['product.pricelist'].create({
'name': 'Company 1 Pricelist',
'company_id': self.company1.id,
})
self.c2_pl = self.env['product.pricelist'].create({
'name': 'Company 2 Pricelist',
'company_id': self.company2.id,
'website_id': False,
})
self.demo_user.partner_id.property_product_pricelist = self.c1_pl
# Switch env.user company to create ir.property in company2
self.env.user.company_id = self.company2
self.demo_user.partner_id.property_product_pricelist = self.c2_pl
# Ensure everything was done correctly
self.assertEqual(self.demo_user.partner_id.with_context(force_company=self.company1.id).property_product_pricelist, self.c1_pl)
self.assertEqual(self.demo_user.partner_id.with_context(force_company=self.company2.id).property_product_pricelist, self.c2_pl)
irp1 = self.env['ir.property'].search([
('name', '=', 'property_product_pricelist'),
('company_id', '=', self.company1.id),
('res_id', '=', 'res.partner,%s' % self.demo_user.partner_id.id),
('value_reference', '=', 'product.pricelist,%s' % self.c1_pl.id),
])
irp2 = self.env['ir.property'].search([
('name', '=', 'property_product_pricelist'),
('company_id', '=', self.company2.id),
('res_id', '=', 'res.partner,%s' % self.demo_user.partner_id.id),
('value_reference', '=', 'product.pricelist,%s' % self.c2_pl.id),
])
self.assertEqual(len(irp1 + irp2), 2, "Ensure there is an `ir.property` for demo partner for every company, and that the pricelist is the company specific one.")
simulate_frontend_context(self)
# ---------------------------------- IR.PROPERTY -------------------------------------
# id | name | res_id | company_id | value_reference
# ------------------------------------------------------------------------------------
# 1 | 'property_product_pricelist' | | 1 | product.pricelist,1
# 2 | 'property_product_pricelist' | | 2 | product.pricelist,2
# 3 | 'property_product_pricelist' | res.partner,8 | 1 | product.pricelist,10
# 4 | 'property_product_pricelist' | res.partner,8 | 2 | product.pricelist,11
def test_property_product_pricelist_multi_company(self):
''' Test that the `property_product_pricelist` of `res.partner` is read
for the company of the website and not the current user company.
This is the case if the user visit a website for which the company
is not the same as its user's company.
Here, as demo user (company1), we will visit website1 (company2).
It should return the ir.property for demo user for company2 and not
for the company1 as we should get the website's company pricelist
and not the demo user's current company pricelist.
'''
# First check: It should return ir.property,4 as company_id is
# website.company_id and not env.user.company_id
company_id = self.website.company_id.id
partner = self.demo_user.partner_id.with_context(force_company=company_id)
demo_pl = partner.property_product_pricelist
self.assertEqual(demo_pl, self.c2_pl)
# Second thing to check: It should not error in read right access error
# Indeed, the ir.rule for pricelists rights about company should allow to
# also read a pricelist from another company if that company is the one
# from the currently visited website.
self.env(user=self.env.ref('base.user_demo'))['product.pricelist'].browse(demo_pl.id).name
+1 -1
View File
@@ -179,7 +179,7 @@
<field name="arch" type="xml">
<field name='currency_id' position='after'>
<field name="selectable"/>
<field name="website_id" options="{'no_create': True}" string="Allow to use on " placeholder="None website"/>
<field name="website_id" options="{'no_create': True}" />
</field>
</field>
</record>
+1 -2
View File
@@ -1574,8 +1574,7 @@
<span t-else="">An error occured while processing your payment. Please try again or contact your administrator.</span>
</t>
<t t-if="payment_tx_id.state == 'authorized'">
<span t-if="payment_tx_id.acquirer_id.auth_msg" t-raw="payment_tx_id.acquirer_id.auth_msg"/>
<span t-else="">Your payment has been authorized.</span>
<span>Your payment has been authorized.</span>
</t>
</div>
<div t-if="payment_tx_id.acquirer_id.post_msg" class="card-body">
+203 -127
View File
@@ -32,7 +32,8 @@ Module structure
Directories
-----------
A module is organised in important directories. Those contain the business logic; having a look at them should make you understand the purpose of the module.
A module is organized in important directories. Those contain the business logic;
having a look at them should make you understand the purpose of the module.
- *data/* : demo and data xml
- *models/* : models definition
@@ -49,108 +50,186 @@ Other optional directories compose the module.
File naming
-----------
For *views* declarations, distinguish the backend views definition (list, form, kanban, ...), the templates (QWeb pages) and bundle (JS and CSS assets). Bundles are theorically QWeb templates, but should be in their own file :file:`assets.xml`.
For *models*, split the business logic by sets of models, in each set
select a main model, this model gives its name to the set. If there is
only one model, its name is the same as the module name. For
each set named <main_model> the following files may be created:
File naming is important to quickly find information through all odoo addons.
This section explains how to name files in a standard odoo module. As an
example we use a plant nursery application. It holds two main models plant.nursery
and plant.order.
- :file:`models/{<main_model>}.py`
- :file:`models/{<inherited_main_model>}.py`
- :file:`views/{<main_model>}_templates.xml`
- :file:`views/{<main_model>}_views.xml`
For instance, *sale* module introduces ``sale_order`` and
``sale_order_line`` where ``sale_order`` is dominant. So the
``<main_model>`` files will be named :file:`models/sale_order.py` and
:file:`views/sale_order_views.py`.
For *data*, split them by purpose : demo or data. The filename will be
the main_model name, suffixed by *_demo.xml* or *_data.xml*.
For *controllers*, the only file should be named *<my_module_name>.py*. If you need to inherit an existing controller from another module, its name will be *<module_name>.py*. Unlike *models*, each controller class should be contained in a separated file. For instance, *purchase* module implements its part of the customer portal in :file:`controllers/portal.py` and its own typical routes on :file:`controllers/purchase.py`.
For *static files*, since the resources can be used in different contexts (frontend, backend, both), they will be included in only one bundle. So, CSS/Less, JavaScript and XML files should be suffixed with the name of the bundle type. i.e.: *im_chat_common.css*, *im_chat_common.js* for 'assets_common' bundle, and *im_chat_backend.css*, *im_chat_backend.js* for 'assets_backend' bundle.
If the module owns only one file, the convention will be *<module_name>.ext* (i.e.: *project.js*).
Don't link data (image, libraries) outside Odoo: do not use an
URL to an image but copy it in our codebase instead.
Regarding *data*, split them by purpose: data or demo. The filename will be
the *main_model* name, suffixed by *_data.xml* or *_demo.xml*.
Regarding *wizards*, naming convention is :
- :file:`{<main_transient>}.py`
- :file:`{<main_transient>}_views.xml`
Where *<main_transient>* is the name of the dominant transient model, just like for *models*. <main_transient>.py can contains the models 'transient_model.action' and 'transient_model.action.line'.
For *statistics reports*, their names should look like :
- :file:`{<report_name_A>}_report.py`
- :file:`{<report_name_A>}_report_views.py` (often pivot and graph views)
For *printable reports*, you should have :
- :file:`{<print_report_name>}_reports.py` (report actions, paperformat definition, ...)
- :file:`{<print_report_name>}_templates.xml` (xml report templates)
The complete tree should look like
Concerning *models*, split the business logic by sets of models belonging to
a same main model. Each set lies in a given file named based on its main model.
If there is only one model, its name is the same as the module name. Each
inherited model should be in its own file to help understanding of impacted
models.
.. code-block:: text
addons/<my_module_name>/
addons/plant_nursery/
|-- models/
| |-- plant_nursery.py (first main model)
| |-- plant_order.py (another main model)
| |-- res_partner.py (inherited Odoo model)
Concerning *security* and access rights and rules two main files should be used.
First one is the definition of access rights done in a ``ir.model.access.csv``
file. User groups are defined in ``<module>_groups.xml``. Access rules are
defined in ``<model>_security.xml``.
.. code-block:: text
addons/plant_nursery/
|-- security/
| |-- ir.model.access.csv
| |-- plant_nusery_groups.xml
| |-- plant_nusery_security.xml
| |-- plant_order_security.xml
Concerning *views*, backend views should be split like models and suffixed
by ``_views.xml``. Backend views are list, form, kanban, activity, graph, pivot, ..
views. To ease split by model in views main menus not linked to specific actions
may be extracted into an optional ``<module>_menus.xml`` file. Templates (QWeb
pages used notably for portal / website display) and bundles (import of JS and
CSS assets) are put in separate files. Those are respectively
``<model>_templates.xml`` and ``assets.xml`` files.
.. code-block:: text
addons/plant_nursery/
|-- views/
| | -- assets.xml (import of JS / CSS)
| | -- plant_nursery_menus.xml (optional definition of main menus)
| | -- plant_nursery_views.xml (backend views)
| | -- plant_nursery_templates.xml (portal templates)
| | -- plant_order_views.xml
| | -- plant_order_templates.xml
| | -- res_partner_views.xml
Concerning *data*, split them by purpose (demo or data) and main model. Filenames
will be the main_model name suffixed by ``_demo.xml`` or ``_data.xml``. For instance
for an application having demo and data for its main model as well as subtypes,
activities and mail templates all related to mail module:
.. code-block:: text
addons/plant_nursery/
|-- data/
| |-- plant_nursery_data.xml
| |-- plant_nursery_demo.xml
| |-- mail_data.xml
Concerning *controllers*, generally all controllers belong to a single controller
contained in a file named ``<module_name>.py``. An old convention in Odoo is to
name this file ``main.py`` but it is considered as outdated. If you need to inherit
an existing controller from another module do it in ``<inherited_module_name>.py``.
For example adding portal controller in an application is done in ``portal.py``.
.. code-block:: text
addons/plant_nursery/
|-- controllers/
| |-- plant_nursery.py
| |-- portal.py (inheriting portal/controllers/portal.py)
| |-- main.py (deprecated, replaced by plant_nursery.py)
Concerning *static files*, Javascript files follow globally the same logic as
python models. Each component should be in its own file with a meaningful name.
For instance, the activity widgets are located in ``activity.js`` of mail module.
Subdirectories can also be created to structure the 'package' (see web module
for more details). The same logic should be applied for the templates of JS
widgets (static XML files) and for their styles (scss files). Don't link
data (image, libraries) outside Odoo: do not use an URL to an image but copy
it in the codebase instead.
Concerning *wizards*, naming convention is the same of for python models:
``<transient>.py`` and ``<transient>_views.xml``. Both are put in the wizard
directory. This naming comes from old odoo applications using the wizard
keyword for transient models.
.. code-block:: text
addons/plant_nursery/
|-- wizard/
| |-- make_plant_order.py
| |-- make_plant_order_views.xml
Concerning *statistics reports* done with python / SQL views and classic views
naming is the following :
.. code-block:: text
addons/plant_nursery/
|-- report/
| |-- plant_order_report.py
| |-- plant_order_report_views.xml
Concerning *printable reports* which contain mainly data preparation and Qweb
templates naming is the following :
.. code-block:: text
addons/plant_nursery/
|-- report/
| |-- plant_order_reports.xml (report actions, paperformat, ...)
| |-- plant_order_templates.xml (xml report templates)
The complete tree of our Odoo module therefore looks like
.. code-block:: text
addons/plant_nursery/
|-- __init__.py
|-- __manifest__.py
|-- controllers/
| |-- __init__.py
| |-- <inherited_module_name>.py
| `-- <my_module_name>.py
| |-- plant_nursery.py
| |-- portal.py
|-- data/
| |-- <main_model>_data.xml
| `-- <inherited_main_model>_demo.xml
| |-- plant_nursery_data.xml
| |-- plant_nursery_demo.xml
| |-- mail_data.xml
|-- models/
| |-- __init__.py
| |-- <main_model>.py
| `-- <inherited_main_model>.py
| |-- plant_nursery.py
| |-- plant_order.py
| |-- res_partner.py
|-- report/
| |-- __init__.py
| |-- <main_stat_report_model>.py
| |-- <main_stat_report_model>_views.xml
| |-- <main_print_report>_reports.xml
| `-- <main_print_report>_templates.xml
| |-- plant_order_report.py
| |-- plant_order_report_views.xml
| |-- plant_order_reports.xml (report actions, paperformat, ...)
| |-- plant_order_templates.xml (xml report templates)
|-- security/
| |-- ir.model.access.csv
| `-- <main_model>_security.xml
| |-- plant_nusery_groups.xml
| |-- plant_nusery_security.xml
| |-- plant_order_security.xml
|-- static/
| |-- img/
| | |-- my_little_kitten.png
| | `-- troll.jpg
| | |-- troll.jpg
| |-- lib/
| | `-- external_lib/
| `-- src/
| |-- js/
| | |-- <my_module_name>.js
`-- <my_widget_A>.js
| |-- css/
| | `-- <my_module_name>.css
| |-- scss/
| | `-- <my_module_name>.scss
| `-- xml/
| |-- <my_module_name>.xml
`-- <my_widget_A>.xml
| | |-- external_lib/
| |-- src/
| | |-- js/
| | | |-- widget_a.js
| | | |-- widget_b.js
| | |-- scss/
| | | |-- widget_a.scss
| | | |-- widget_b.scss
| | |-- xml/
| | | |-- widget_a.xml
| | | |-- widget_a.xml
|-- views/
| |-- <main_model>_templates.xml
| |-- <main_model>_views.xml
| |-- <inherited_main_model>_templates.xml
| `-- <inherited_main_model>_views.xml
`-- wizard/
|-- <main_transient_A>.py
|-- <main_transient_A>_views.xml
|-- <main_transient_B>.py
`-- <main_transient_B>_views.xml
| |-- assets.xml
| |-- plant_nursery_menus.xml
| |-- plant_nursery_views.xml
| |-- plant_nursery_templates.xml
| |-- plant_order_views.xml
| |-- plant_order_templates.xml
| |-- res_partner_views.xml
|-- wizard/
| |--make_plant_order.py
| |--make_plant_order_views.xml
.. note:: File names should only contain ``[a-z0-9_]`` (lowercase
alphanumerics and ``_``)
@@ -201,8 +280,8 @@ Odoo supports custom tags acting as syntactic sugar:
The 4 first tags are prefered over the *record* notation.
Naming xml_id
-------------
XML IDs and naming
------------------
Security, View and Action
~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -214,9 +293,10 @@ Use the following pattern :
``kanban``, ``form``, ``tree``, ``search``, ...
* For an action: the main action respects :samp:`{<model_name>}_action`.
Others are suffixed with :samp:`_{<detail>}`, where *detail* is a
lowercase string briefly explaining the action.
This is used only if multiple actions are declared for the
model.
lowercase string briefly explaining the action. This is used only if
multiple actions are declared for the model.
* For window actions: suffix the action name by the specific view information
like :samp:`{<model_name>}_action_view_{<view_type}`.
* For a group: :samp:`{<model_name>}_group_{<group_name>}` where *group_name*
is the name of the group, generally 'user', 'manager', ...
* For a rule: :samp:`{<model_name>}_rule_{<concerned_group>}` where
@@ -224,17 +304,33 @@ Use the following pattern :
for the 'model_name_group_user', 'public' for public user, 'company'
for multi-company rules, ...).
Name should be identical to xml id with dots replacing underscores. Actions
should have a real naming as it is used as display name.
.. code-block:: xml
<!-- views and menus -->
<!-- views -->
<record id="model_name_view_form" model="ir.ui.view">
<field name="name">model.name.view.form</field>
...
</record>
<record id="model_name_view_kanban" model="ir.ui.view">
<field name="name">model.name.view.kanban</field>
...
</record>
<!-- actions -->
<record id="model_name_action" model="ir.act.window">
<field name="name">Model Main Action</field>
...
</record>
<record id="model_name_action_child_list" model="ir.actions.act_window">
<field name="name">Model Access Childs</field>
</record>
<!-- menus and sub-menus -->
<menuitem
id="model_name_menu_root"
name="Main Menu"
@@ -248,15 +344,6 @@ Use the following pattern :
sequence="10"
/>
<!-- actions -->
<record id="model_name_action" model="ir.actions.act_window">
...
</record>
<record id="model_name_action_child_list" model="ir.actions.act_window">
...
</record>
<!-- security -->
<record id="module_name_group_user" model="res.groups">
...
@@ -270,30 +357,31 @@ Use the following pattern :
...
</record>
Inheriting XML
~~~~~~~~~~~~~~
Xml Ids of inheriting views should use the same ID as the original record.
It helps finding all inheritance at a glance. As final Xml Ids are prefixed
by the module that creates them there is no overlap.
.. note:: View names use dot notation ``my.model.view_type`` or
``my.model.view_type.inherit`` instead of *"This is the form view of
My Model"*.
Inherited XML
~~~~~~~~~~~~~
The naming pattern of inherited view is
#. Extension mode: Use the same xml id than the original view you are extending, and suffix it by :samp:`{_inherit}` . For instance, the view :samp:`project.project_view_form` can be extended by :samp:`project_forecast.project_view_form_inherit`.
#. Primary mode: Keep the original xml id.
Naming should contain an ``.inherit.{details}`` suffix to ease understanding
the override purpose when looking at its name.
.. code-block:: xml
<record id="module2.model_view_form_inherit" model="ir.ui.view">
<record id="model_view_form" model="ir.ui.view">
<field name="name">model.view.form.inherit.module2</field>
<field name="inherit_id" ref="module1.model_view_form"/>
...
</record>
New primary views do not require the inherit suffix as those are new records
based upon the first one.
.. code-block:: xml
<record id="module2.model_view_form" model="ir.ui.view">
<field name="name">model.view.form.module2</field>
<field name="inherit_id" ref="module1.model_view_form"/>
<field name="mode">primary</field>
...
@@ -311,11 +399,6 @@ source code tries to respect Python standard, but some of them can be ignored.
- E501: line too long
- E301: expected 1 blank line, found 0
- E302: expected 2 blank lines, found 1
- E126: continuation line over-indented for hanging indent
- E123: closing bracket does not match indentation of opening bracket's line
- E127: continuation line over-indented for visual indent
- E128: continuation line under-indented for visual indent
- E265: block comment should start with '# '
Imports
-------
@@ -843,7 +926,7 @@ Symbols and Conventions
- Variable name :
- use camelcase for model variable
- use underscore lowercase notation for common variable.
- prefix your variable name with *_id* or *_ids* if it contains a record id or list of id. Don't use ``partner_id`` to contain a record of res.partner
- suffix your variable name with *_id* or *_ids* if it contains a record id or list of id. Don't use ``partner_id`` to contain a record of res.partner
.. code-block:: python
@@ -964,17 +1047,10 @@ Javascript coding guidelines
- Use a linter (jshint, ...)
- Never add minified Javascript Libraries
- Use camelcase for class declaration
- Unless your code is supposed to run on every page, target specific pages
using the ``if_dom_contains`` function of website module. Target an
element which is specific to the pages your code needs to run on
using JQuery.
.. code-block:: javascript
odoo.website.if_dom_contains('.jquery_class_selector', function () {
/*your code here*/
});
More precise JS guidelines are detailed at https://github.com/odoo/odoo/wiki/Javascript-coding-guidelines.
You may also have a look at existing API in Javascript by looking Javascript
References.
CSS coding guidelines
---------------------
+6 -4
View File
@@ -29,7 +29,7 @@
# Pierre-Antoine Magnet <pma@odoo.com>, 2017
# AleEscandon <alejandra.escandon@jarsa.com.mx>, 2017
# Julián Andrés Osorio López <mcjulian1107@gmail.com>, 2017
# e2f_es_ t6 <e2f_es_t6@outlook.com>, 2017
# b9b2db555321dc69d8faed263d46c5b6, 2017
# Andres Ruiz <andres.ruiz.otalora@gmail.com>, 2017
# Raquel Iciarte <ric@odoo.com>, 2017
# Nacho Hermoso <nacho@domatix.com>, 2017
@@ -38,7 +38,7 @@
# Gustavo Valverde, 2018
# JOSE ALEJANDRO ECHEVERRI VALENCIA <josealejandroeche@gmail.com>, 2018
# e2f <projects@e2f.com>, 2018
# e2f_EPV Translator <e2f_EPV@outlook.com>, 2018
# 966ff43e6966712895a590e7320ca288, 2018
# José Vicente <txusev@gmail.com>, 2018
# Luis M. Ontalba <luis.martinez@tecnativa.com>, 2018
# RGB Consulting <odoo@rgbconsulting.com>, 2018
@@ -46,7 +46,7 @@
# Martin Trigaux, 2018
# Galileo Sanchez <galileopy@gmail.com>, 2018
# Fernando San Martín Woerner <fsnmartin@gmail.com>, 2018
# ES R1 <e2fes@hotmail.com>, 2018
# ced903afe967c2a46d1036224972db9d, 2018
# Cristian Salamea <ovnicraft@gmail.com>, 2018
# Pedro M. Baeza <pedro.baeza@gmail.com>, 2018
# Christopher Ormaza <chris.ormaza@gmail.com>, 2018
@@ -60,6 +60,7 @@
# Jesús Alan Ramos Rodríguez <alan.ramos@jarsa.com.mx>, 2018
# Rodrigo Aragon <conectacoatepec@gmail.com>, 2018
# Gabriel Umana <gabriel.umana@delfixcr.com>, 2019
# Jesse Garza <jga@odoo.com>, 2019
#
msgid ""
msgstr ""
@@ -67,7 +68,7 @@ msgstr ""
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-08-02 10:02+0000\n"
"PO-Revision-Date: 2016-08-05 12:54+0000\n"
"Last-Translator: Gabriel Umana <gabriel.umana@delfixcr.com>, 2019\n"
"Last-Translator: Jesse Garza <jga@odoo.com>, 2019\n"
"Language-Team: Spanish (https://www.transifex.com/odoo/teams/41243/es/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -8945,6 +8946,7 @@ msgstr "Cuenta bancaria"
msgid ""
"Bank account type: Normal or IBAN. Inferred from the bank account number."
msgstr ""
"Tipo de cuenta bancaria: Normal o IBAN. Se infiere del número de cuenta."
#. module: base
#: model:ir.model.fields,help:base.field_res_company__bank_ids
+7 -4
View File
@@ -6459,7 +6459,7 @@ msgstr "Данс Эзэмшигч"
#. module: base
#: model:ir.model.fields,field_description:base.field_res_partner_bank__acc_holder_name
msgid "Account Holder Name"
msgstr ""
msgstr "Данс эзэмшигчийн нэр"
#. module: base
#: model:ir.model.fields,field_description:base.field_res_company__account_no
@@ -6479,7 +6479,7 @@ msgstr "Дансны дугаар давтагдах ёсгүй"
#. module: base
#: model:ir.module.module,shortdesc:base.module_account_taxcloud
msgid "Account TaxCloud"
msgstr ""
msgstr "TaxCloud"
#. module: base
#: model:ir.module.module,shortdesc:base.module_website_sale_account_taxcloud
@@ -6502,6 +6502,8 @@ msgid ""
"Account holder name, in case it is different than the name of the Account "
"Holder"
msgstr ""
"Данс эзэмшигчийн нэр, Данс эзэмшигчийн бүртгэлтэй нэр нь Odoo дээрх нэрээс "
"өөр байх тохиолдолд энэ талбарыг ашиглана"
#. module: base
#: model:ir.module.category,name:base.module_category_accounting
@@ -7513,6 +7515,7 @@ msgstr "Банкны данс"
msgid ""
"Bank account type: Normal or IBAN. Inferred from the bank account number."
msgstr ""
"Банкны дансны төрөл: Энгийн эсвэл IBAN. Банкны дансны дугаараар ялгагддаг."
#. module: base
#: model:ir.model.fields,help:base.field_res_company__bank_ids
@@ -9014,7 +9017,7 @@ msgstr "Цэс үүсгэх"
#. module: base
#: model:ir.actions.act_window,help:base.action_res_partner_bank_account_form
msgid "Create a bank account"
msgstr ""
msgstr "Банкны данс үүсгэх"
#. module: base
#: model:ir.actions.act_window,help:base.action_ui_view_custom
@@ -17069,7 +17072,7 @@ msgstr "Толгой цэс"
#: model:ir.model.fields,field_description:base.field_ir_ui_menu__parent_path
#: model:ir.model.fields,field_description:base.field_res_partner_category__parent_path
msgid "Parent Path"
msgstr ""
msgstr "Эцэг зам"
#. module: base
#: model:ir.model.fields,field_description:base.field_res_partner__parent_name
+13 -745
View File
@@ -888,37 +888,6 @@ msgid ""
" try to use this method on an invoice for whose payment no mandate can be used.\n"
" "
msgstr ""
"\n"
" This module enables the generation of SEPA Direct Debit (SDD)-compliant XML files (consistent\n"
" with pain.008.001.02 specification) to send to your bank in order to\n"
" collect a set of payments.\n"
"\n"
" To be elligible for this payment method, a customer must have first\n"
" returned a mandate to the company, giving his consent to use direct debit.\n"
" This consent must have been encoded as a 'customer mandate' into Odoo.\n"
"\n"
" You also need to meet the following requirements in order to properly\n"
" generate a SDD file:\n"
" - Your company account must be set to a valid IBAN number\n"
" - Your company account must be associated to a bank having a valid BIC\n"
" - Your company must have been given a creditor identifier (this can be done in the 'settings' menu of the accounting module)\n"
" - Your company must have defined a journal to receive SDD payments (again, in 'settings' of accounting module)\n"
" - Every customer for which you generate a payment must have a valid IBAN account number in a bank associated with a correct BIC.\n"
"\n"
" Odoo will let you know if any of these requirements are not satisfied.\n"
"\n"
" Any invoice that gets validated for a customer with a mandate will be\n"
" automatically set in 'paid' state, and a payment will be generated. An\n"
" option in the dashboard will then allow you to view all the payments generated\n"
" via SDD and to generate a XML collection file for them, grouping them as\n"
" you see fit.\n"
"\n"
" A dedicated 'SEPA Direct Debit' payment method is also available for\n"
" open invoices, when selecting a bank account in the 'register payment' wizard.\n"
" Use it to generate a SDD payment for the invoices if you added a mandate\n"
" for its owner after its validation. You will see an error message if you\n"
" try to use this method on an invoice for whose payment no mandate can be used.\n"
" "
#. module: base
#: model:ir.module.module,summary:base.module_account_online_sync
@@ -3105,22 +3074,6 @@ msgid ""
" - Yearly Salary by Head and Yearly Salary by Employee Report\n"
" "
msgstr ""
"\n"
"Indian Payroll Salary Rules.\n"
"============================\n"
"\n"
" -Configuration of hr_payroll for India localization\n"
" -All main contributions rules for India payslip.\n"
" * New payslip report\n"
" * Employee Contracts\n"
" * Allow to configure Basic / Gross / Net Salary\n"
" * Employee PaySlip\n"
" * Allowance / Deduction\n"
" * Integrated with Leaves Management\n"
" * Medical Allowance, Travel Allowance, Child Allowance, ...\n"
" - Payroll Advice and Report\n"
" - Yearly Salary by Head and Yearly Salary by Employee Report\n"
" "
#. module: base
#: model:ir.module.module,description:base.module_mail_github
@@ -3697,16 +3650,16 @@ msgid ""
"\n"
msgstr ""
"\n"
"Module om betaalherinneringen te automatiseren voor onbetaalde facturen, op basis van meerdere aanmaan niveau's\n"
"Module om betaalherinneringen te automatiseren voor onbetaalde facturen, op basis van meerdere aanmaningsniveau's\n"
"================================================== ======================= \n"
"\n"
"U kunt uw meerdere niveaus van definiëren:.\n"
"U kunt uw meerdere niveaus van aanmanen definiëren:.\n"
"-------------------------------------------------- ------------- \n"
"     Instellingen / Beheer / Betaalherinnering niveau's\n"
"     \n"
"Zodra het is gedefinieerd, kunt u elke dag automatisch afdrukken herinneringen door simpelweg op het menu te klikken:.\n"
"Instellingen / Beheer / Betaalherinnering niveau's\n"
"\n"
"Zodra het is gedefinieerd, kunt u elke dag automatisch herinneringen afdrukken door simpelweg op het menu te klikken:\n"
"-------------------------------------------------- -------------------------------------------------- --\n"
"     Klantoverzicht\n"
"Klantoverzicht\n"
"\n"
"Het zal een PDF / e-mails versturen / handmatige acties genereren volgens de verschillende niveaus\n"
"van herinnering. U kunt verschillende instellingen maken voor voor verschillende bedrijven.\n"
@@ -4645,7 +4598,7 @@ msgstr ""
"==========================\n"
"\n"
"Maak een aantal producten waarvoor u de declaraties opnieuw kunt factureren.\n"
"Met deze module kunt u de declaraties factureren aan klanten, dit door de verkooporder in te stellen op de declaratie..\n"
"Met deze module kunt u de declaraties factureren aan klanten, dit door de verkooporder in te stellen op de declaratie.\n"
#. module: base
#: model:ir.module.module,description:base.module_fetchmail
@@ -5781,7 +5734,7 @@ msgstr ""
"Deze module voegt de vereiste basiscode toe voor een volledig geïntegreerd klantenportaal. Het bevat de basiscontrollerklasse en basissjablonen. Zakelijke add-ons\n"
"zullen hun specifieke sjablonen en controllers toevoegen om de klantportaal uit te breiden.\n"
"\n"
"Deze module bevat voornamelijke code afkomstig van Odoo v10 website_portal. Doel\n"
"Deze module bevat voornamelijk code afkomstig van Odoo v10 website_portal. Doel\n"
"van deze module is om de weergave van een klantportaal zonder \n"
"afhankelijkheid van de mogelijkheden van de website editie en aanpas mogelijkheden."
@@ -6452,10 +6405,10 @@ msgstr ""
"Deze module voegt een link toe aan het Transifex-project in de vertaalweergave.\n"
"Het doel van deze module is om vertalingen van de hoofdmodules te versnellen.\n"
"\n"
"Om te laten werken gebruikt Odoo Transifex-configuratiebestanden `.txt / config` om de\n"
"Om dit te laten werken gebruikt Odoo Transifex-configuratiebestanden `.txt / config` om de\n"
"projectbron te detecteren. Aangepaste modules worden niet vertaald (sinds deze niet gepubliceerd zijn op het belangrijkste Transifex-project).\n"
"\n"
"De taal die de gebruiker probeert te vertalen moet op de Transifex geactiveerd zijn.\n"
"De taal die de gebruiker probeert te vertalen moet op Transifex geactiveerd zijn.\n"
" "
#. module: base
@@ -6751,7 +6704,7 @@ msgstr ""
"Website Presentaties integratie voor de helpdesk module\n"
"================================================== \n"
"\n"
"     Voeg presentaties toe aan uw team, zodat klanten die hulp zoeken, deze kunnen zien voordat ze nieuwe tickets plaatsen"
"Voeg presentaties toe aan uw team, zodat klanten die hulp zoeken, deze kunnen zien voordat ze nieuwe tickets plaatsen."
#. module: base
#: model:ir.module.module,description:base.module_website_membership
@@ -15708,45 +15661,6 @@ msgid ""
"allowing you to easily find for specific skills and build up a database of\n"
"profiles.\n"
msgstr ""
"Manage Recruitment and Job applications\n"
"---------------------------------------\n"
"\n"
"Publish, promote and organize your job offers with the Odoo\n"
"<a href=\"https://www.odoo.com/page/recruitment\">Open Source Recruitment Application</a>.\n"
"\n"
"Organize your job board, promote your job announces and keep track of\n"
"application submissions easily. Follow every applicant and build up a database\n"
"of skills and profiles with indexed documents.\n"
"\n"
"Post Your Jobs on Best Job Boards\n"
"---------------------------------\n"
"\n"
"Connect automatically to most famous job board websites; linkedIn, Monster,\n"
"Craigslist, ... Every job position has a new email address automatically\n"
"assigned to route applications automatically to the right job position.\n"
"\n"
"Whether applicants contact you by email or using an online form, you get all\n"
"the data indexed automatically (resumes, motivation letter) and you can answer\n"
"in just a click, reusing templates of answers.\n"
"\n"
"Customize Your Recruitment Process\n"
"----------------------------------\n"
"\n"
"Use the kanban view and customize the steps of your recruitments process;\n"
"pre-qualification, first interview, second interview, negociaiton, ...\n"
"\n"
"Get accurate statistics on your recruitment pipeline. Get reports to compare\n"
"the performance of your different investments on external job boards.\n"
"\n"
"Streamline Your Recruitment Process\n"
"-----------------------------------\n"
"\n"
"Follow applicants in your recruitment process with the smart kanban view. Save\n"
"time by automating some communications with email templates.\n"
"\n"
"Documents like resumes and motivation letters are indexed automatically,\n"
"allowing you to easily find for specific skills and build up a database of\n"
"profiles.\n"
#. module: base
#: model:ir.actions.act_window,help:base.grant_menu_access
@@ -17052,103 +16966,6 @@ msgid ""
"Compare revenues with forecasts and budgets in real time.\n"
"\n"
msgstr ""
"Odoo CRM\n"
"--------\n"
"\n"
"Boost sales productivity, improve win rates, grow revenue with the Odoo\n"
"<a href=\"https://www.odoo.com/page/crm\">Open Source CRM</a>.\n"
"\n"
"Manage your sales funnel with no effort. Attract leads, follow-up on phone\n"
"calls and meetings. Analyse the quality of your leads to make informed\n"
"decisions and save time by integrating emails directly into the application.\n"
"\n"
"Your Sales Funnel, The Way You Like It\n"
"--------------------------------------\n"
"\n"
"Track your opportunities pipeline with the revolutionary kanban view. Work\n"
"inside your sales funnel and get instant visual information about next actions,\n"
"new messages, top opportunities and expected revenues.\n"
"\n"
"Lead Management Made Easy\n"
"-------------------------\n"
"\n"
"Create leads automatically from incoming emails. Analyse leads efficiency and\n"
"compare performance by campaigns, channels or sales channel.\n"
"\n"
"Find duplicates, merge leads and assign them to the right salesperson in one\n"
"operation. Spend less time on administration and more time on qualifying leads.\n"
"\n"
"Organize Your Opportunities\n"
"---------------------------\n"
"\n"
"Get your opportunities organized to stay focused on the best deals. Manage all\n"
"your customer interactions from the opportunity like emails, phone calls,\n"
"internal notes, meetings and quotations.\n"
"\n"
"Follow opportunities that interest you to get notified upon specific events:\n"
"deal won or lost, stage changed, new customer demand, etc.\n"
"\n"
"Email Integration and Automation\n"
"--------------------------------\n"
"\n"
"Work with the email applications you already use every day. Whether your\n"
"company uses Microsoft Outlook or Gmail, no one needs to change the way they\n"
"work, so everyone stays productive.\n"
"\n"
"Route, sort and filter incoming emails automatically. Odoo CRM handles incoming\n"
"emails and route them to the right opportunities or sales channel. New leads are\n"
"created on the fly and interested salespersons are notified automatically.\n"
"\n"
"Collaborative Agenda\n"
"--------------------\n"
"\n"
"Schedule your meetings and phone calls using the integrated calendar. You can\n"
"see your agenda and your colleagues' in one view. As a manager, it's easy to\n"
"see what your team is busy with.\n"
"\n"
"Lead Automation and Marketing Campaigns\n"
"---------------------------------------\n"
"\n"
"Drive performance by automating tasks with Odoo <a href=\"https://www.odoo.com/page/crm\">CRM</a>.\n"
"\n"
"Use our marketing campaigns to automate lead acquisition, follow ups and\n"
"promotions. Define automated actions (e.g. ask a salesperson to call, send an\n"
"email, ...) based on triggers (no activity since 20 days, answered a\n"
"promotional email, etc.)\n"
"\n"
"Optimize campaigns from lead to close, on every channel. Make smarter decisions\n"
"about where to invest and show the impact of your marketing activities on your\n"
"company's bottom line.\n"
"\n"
"Customize Your Sales Cycle\n"
"--------------------------\n"
"\n"
"Customize your sales cycle by configuring sales stages that perfectly fit your\n"
"sales approach. Control statistics to get accurate forecasts to improve your\n"
"sales performance at every stage of your customer relationship.\n"
"\n"
"Drive Engagement with Gamification\n"
"----------------------------------\n"
"\n"
"### Leverage your team's natural desire for competition\n"
"\n"
"Reinforce good habits and improve win rates with real-time recognition and\n"
"rewards inspired by [game mechanics](http://en.wikipedia.org/wiki/Gamification).\n"
"Align sales channels around clear business objectives with challenges, personal\n"
"objectives and team leader boards.\n"
"\n"
"### Leaderboards\n"
"\n"
"Promote leaders and competition amongst sales channel with performance ratios.\n"
"\n"
"### Personal Objectives\n"
"\n"
"Assign clear goals to users to align them with the company objectives.\n"
"\n"
"### Team Targets\n"
"\n"
"Compare revenues with forecasts and budgets in real time.\n"
"\n"
#. module: base
#: selection:ir.module.module,license:0
@@ -17213,60 +17030,6 @@ msgid ""
"for your own company. Follow people, share best practices, 'like' top ideas,\n"
"etc.\n"
msgstr ""
"Odoo Enterprise Social Network\n"
"------------------------------\n"
"\n"
"Connect with experts, follow what interests you, share documents and promote\n"
"best practices with Odoo <a href=\"https://www.odoo.com/page/enterprise-social-network\">Enterprise Social Network</a>. Get work done with\n"
"effective collaboration across departments, geographies, documents and business\n"
"applications. All of this while decreasing email overload.\n"
"\n"
"Connect with experts\n"
"--------------------\n"
"\n"
"Next time you have a question for the marketing, sales, R&D or any other\n"
"department, don't send an email blast-post the question to Odoo and get answers\n"
"from the right persons.\n"
"\n"
"Follow what interests you\n"
"-------------------------\n"
"\n"
"Want to get informed about new product features, hot deals, bottlenecks in\n"
"projects or any other event? Just follow what interests you to get the\n"
"information you need what you need; no more, no less.\n"
"\n"
"Get Things Done\n"
"---------------\n"
"\n"
"You can process (not only read) the inbox and easily mark messages for future\n"
"actions. Start feeling the pleasure of having an empty inbox every day; no more\n"
"overload of information.\n"
"\n"
"Promote best practices\n"
"----------------------\n"
"\n"
"Cut back on meetings and email chains by working together in groups of\n"
"interests. Create a group to let people share files, discuss ideas, and vote to\n"
"promote best practices.\n"
"\n"
"Improve Access to Information and Expertise\n"
"-------------------------------------------\n"
"\n"
"Break down information silos. Search across your existing systems to find the\n"
"answers and expertise you need to complete projects quickly.\n"
"\n"
"Collaborate securely\n"
"--------------------\n"
"\n"
"Set the right security policy; public, private or on invitation only --\n"
"according to the information sensitivity.\n"
"\n"
"A Twitter-like Network For My Company\n"
"---------------------------------------\n"
"\n"
"Make every employee feel more connected and engaged with twitter-like features\n"
"for your own company. Follow people, share best practices, 'like' top ideas,\n"
"etc.\n"
#. module: base
#: model:ir.module.module,description:base.module_hr
@@ -17364,98 +17127,6 @@ msgid ""
"Compare revenues with forecasts and budgets in real time.\n"
"\n"
msgstr ""
"Odoo Human Resources\n"
"--------------------\n"
"\n"
"With Odoo <a href=\"https://www.odoo.com/page/employees\">Human Resources</a>,\n"
"manage the most important asset in your company: People\n"
"\n"
"Get all your HR operations managed easily: knowledge sharing, recruitments,\n"
"appraisals, timesheets, contracts, attendances, payroll, etc.\n"
"\n"
"Each need is provided by a specific app that you activate on demand.\n"
"\n"
"Manage Your Employees\n"
"---------------------\n"
"\n"
"Oversee all important information in your company address book. Some\n"
"information are restricted to HR managers, others are public to easily look\n"
"colleagues.\n"
"\n"
"Record employee contracts and get alerts when they have to be renewed.\n"
"\n"
"Streamline Your Recruitment Process\n"
"-----------------------------------\n"
"\n"
"Index resumes, track applicants, search profiles with <a href=\"https://www.odoo.com/page/employees\">Odoo HR</a>.\n"
"\n"
"Post job offers and keep track of each application received. Follow applicants\n"
"in your recruitment process with the smart kanban view.\n"
"\n"
"Save time by automating some communications with email templates. Resumes are\n"
"indexed automatically, allowing you to easily find for specific profiles.\n"
"\n"
"Enterprise Social Network\n"
"-------------------------\n"
"\n"
"Break down information silos. Share knowledge and best practices amongst all\n"
"employees. Follow specific people or documents and join groups of interests to\n"
"share expertise and documents.\n"
"\n"
"Interact with your coworkers in real time with website live chat.\n"
"\n"
"Track time and attendances\n"
"--------------------------\n"
"\n"
"Keep track of the time spent by project, client or task. It's easy to record\n"
"timesheets or check attendances for each employee. Get your analytic accounting\n"
"posted automatically based on time spent on your projects.\n"
"\n"
"Leaves Management\n"
"-----------------\n"
"\n"
"Keep track of the vacation days accrued by each employee. Employees enter their\n"
"requests (paid leaves, sick leaves, etc), for managers to approve and\n"
"validate. It's all done in just a few clicks. The agenda of each employee is\n"
"updated accordingly.\n"
"\n"
"Keep Track of Employee Expenses\n"
"-------------------------------\n"
"\n"
"Get rid of the paper work and follow employee's expenses directly in Odoo.\n"
"Don't loose time or money by controlling the full flow: expense validation,\n"
"reimbursement of employees, posting in the accounting and re-invoicing to\n"
"customers.\n"
"\n"
"Follow Periodic Appraisals\n"
"--------------------------\n"
"\n"
"Set-up appraisals plans and/or surveys for your employees and watch their\n"
"evolution. Define steps for interviews and Odoo will notify managers or\n"
"subordinates automatically to prepare appraisals. Keep track of the progress of\n"
"your staff periodically.\n"
"\n"
"Boost Engagement With Gamification\n"
"----------------------------------\n"
"\n"
"### Define clear objective and provide real time feedback\n"
"\n"
"Inspire achievement with challenges, goals and rewards. Define clear objectives\n"
"and provide real time feedback and tangible results. Showcase the top\n"
"performers to the entire channel and publicly recognize a job well done.\n"
"\n"
"### Leaderboards\n"
"\n"
"Promote leaders and competition amongst sales channel with performance ratios.\n"
"\n"
"### Personal Objectives\n"
"\n"
"Assign clear goals to users to align them with the company objectives.\n"
"\n"
"### Team Targets\n"
"\n"
"Compare revenues with forecasts and budgets in real time.\n"
"\n"
#. module: base
#: model:ir.module.module,description:base.module_mrp
@@ -17995,69 +17666,6 @@ msgid ""
"etc.\n"
"\n"
msgstr ""
"Odoo Supply Chain\n"
"-----------------\n"
"\n"
"Automate requisition-to-pay, control invoicing with the Odoo\n"
"<a href=\"https://www.odoo.com/page/purchase\">Open Source Supply Chain</a>.\n"
"\n"
"Automate procurement propositions, launch request for quotations, track\n"
"purchase orders, manage vendors' information, control products reception and\n"
"check vendors' invoices.\n"
"\n"
"Automated Procurement Propositions\n"
"----------------------------------\n"
"\n"
"Reduce inventory level with procurement rules. Get the right purchase\n"
"proposition at the right time to reduce your inventory level. Improve your\n"
"purchase and inventory performance with procurement rules depending on stock\n"
"levels, logistic rules, sales orders, forecasted manufacturing orders, etc.\n"
"\n"
"Send requests for quotations or purchase orders to your vendor in one click.\n"
"Get access to product receptions and invoices from your purchase order.\n"
"\n"
"Purchase Tenders\n"
"----------------\n"
"\n"
"Launch purchase tenders, integrate vendor's answers in the process and\n"
"compare propositions. Choose the best offer and send purchase orders easily.\n"
"Use reporting to analyse the quality of your vendors afterwards.\n"
"\n"
"\n"
"Email integrations\n"
"------------------\n"
"\n"
"Integrate all vendor's communications on the purchase orders (or RfQs) to get\n"
"a strong traceability on the negotiation or after sales service issues. Use the\n"
"claim management module to track issues related to vendors.\n"
"\n"
"Standard Price, Average Price, FIFO\n"
"-----------------------------------\n"
"\n"
"Use the costing method that reflects your business: standard price, average\n"
"price, fifo or lifo. Get your accounting entries and the right inventory\n"
"valuation in real-time; Odoo manages everything for you, transparently.\n"
"\n"
"Import Vendor Pricelists\n"
"--------------------------\n"
"\n"
"Take smart purchase decisions using the best prices. Easily import vendor's\n"
"pricelists to make smarter purchase decisions based on promotions, prices\n"
"depending on quantities and special contract conditions. You can even base your\n"
"sale price depending on your vendor's prices.\n"
"\n"
"Control Products and Invoices\n"
"-----------------------------\n"
"\n"
"No product or order is left behind, the inventory control allows you to manage\n"
"back orders, refunds, product reception and quality control. Choose the right\n"
"control method according to your need.\n"
"\n"
"Control vendor bills with no effort. Choose the right method according to\n"
"your need: pre-generate draft invoices based on purchase orders, on products\n"
"receptions, create invoices manually and import lines from purchase orders,\n"
"etc.\n"
"\n"
#. module: base
#: model:ir.module.module,shortdesc:base.module_web_diagram
@@ -18185,123 +17793,6 @@ msgid ""
"\n"
"Schedule, organize, promote or sell events online; conferences, trainings, webinars, etc.\n"
msgstr ""
"Odoo Website Builder\n"
"--------------------\n"
"\n"
"Get an awesome and <a href=\"https://www.odoo.com/page/website-builder\">free website</a>,\n"
"easily customizable with the Odoo <a href=\"https://www.odoo.com/page/website-builder\">website builder</a>.\n"
"\n"
"Create enterprise grade website with our super easy builder. Use finely\n"
"designed building blocks and edit everything inline.\n"
"\n"
"Benefit from out-of-the-box business features; e-Commerce, events, blogs, jobs\n"
"announces, customer references, call-to-actions, etc.\n"
"\n"
"Edit Anything Inline\n"
"--------------------\n"
"\n"
"Create beautiful websites with no technical knowledge. Odoo's unique *'edit\n"
"inline'* approach makes website creation surprisingly easy. No more complex\n"
"backend; just click anywhere to change any content.\n"
"\n"
"\"Want to change the price of a product? or put it in bold? Want to change a\n"
"blog title?\" Just click and change. What you see is what you get. Really.\n"
"\n"
"Awesome. Astonishingly Beautiful.\n"
"---------------------------------\n"
"\n"
"Odoo's building blocks allow to design modern websites that are not possible\n"
"with traditional WYSIWYG page editors.\n"
"\n"
"Whether it's for products descriptions, blogs or static pages, you don't need\n"
"to be a professioanl designer to create clean contents. Just drag and drop and\n"
"customize predefined building blocks.\n"
"\n"
"Enterprise-Ready, out-of-the-box\n"
"--------------------------------\n"
"\n"
"Activate ready-to-use enterprise features in just a click; e-commerce,\n"
"call-to-actions, jobs announces, events, customer references, blogs, etc.\n"
"\n"
"Traditional eCommerce and CMS have poorly designed backends as it's not their\n"
"core focus. With the Odoo integration, you benefit from the best management\n"
"software to follow-up on your orders, your jobs applicants, your leads, etc.\n"
"\n"
"A Great Mobile Experience\n"
"-------------------------\n"
"\n"
"Get a mobile friendly website thanks to our responsive design based on\n"
"bootstrap. All your pages adapt automatically to the screen size. (mobile\n"
"phones, tablets, desktop) You don't have to worry about mobile contents, it\n"
"works by default.\n"
"\n"
"SEO tools at your finger tips\n"
"-----------------------------\n"
"\n"
"The *Promote* tool suggests keywords according to Google most searched terms.\n"
"Search Engine Optimization tools are ready to use, with no configuration\n"
"required.\n"
"\n"
"Google Analytics tracks your shopping cart events by default. Sitemap and\n"
"structured content are created automatically for Google indexation.\n"
"\n"
"Multi-Languages Made Easy\n"
"-------------------------\n"
"\n"
"Get your website translated in multiple languages with no effort. Odoo proposes\n"
"and propagates translations automatically across pages, following what you edit\n"
"on the master page.\n"
"\n"
"Benefit from professional translators to translate all your contents\n"
"automatically with the [Gengo](http://gengo.com) integration. Update any part\n"
"of your website and the translated versions are pushed automatically in a few\n"
"hours.\n"
"\n"
"Designer-Friendly Templates\n"
"---------------------------\n"
"\n"
"Templates are awesome and easy to design. You don't need to develop to create\n"
"new pages, themes or building blocks. We use a clean HTML structure, a\n"
"[bootstrap](http://getbootstrap.com/) CSS.\n"
"\n"
"Customize every page on the fly with the integrated template editor. Distribute\n"
"your work easily as an Odoo module.\n"
"\n"
"Fluid Grid Layouting\n"
"--------------------\n"
"\n"
"Design perfect pages by drag and dropping building blocks. Move and scale them\n"
"to fit the layout you are looking for.\n"
"\n"
"Building blocks are based on a responsive, mobile friendly fluid grid system\n"
"that appropriately scales up to 12 columns as the device or viewport size\n"
"increases.\n"
"\n"
"Professional Themes\n"
"-------------------\n"
"\n"
"Design a custom theme or reuse pre-defined themes to customize the look and\n"
"feel of your website.\n"
"\n"
"Test new color scheme easily; you can change your theme at any time in just a\n"
"click.\n"
"\n"
"Integrated With Odoo Apps\n"
"-------------------------\n"
"\n"
"### e-Commerce\n"
"\n"
"Promote products, sell online, optimize visitors' shopping experience.\n"
"\n"
"\n"
"### Blogs\n"
"\n"
"Write news, attract new visitors, build customer loyalty.\n"
"\n"
"\n"
"### Online Events\n"
"\n"
"Schedule, organize, promote or sell events online; conferences, trainings, webinars, etc.\n"
#. module: base
#: model:ir.module.module,description:base.module_website_sale
@@ -18461,160 +17952,6 @@ msgid ""
"Schedule, organize, promote or sell events online; conferences, webinars, trainings, etc.\n"
"\n"
msgstr ""
"Odoo e-Commerce\n"
"---------------\n"
"\n"
"### Optimize sales with an awesome online store.\n"
"\n"
"Odoo is an <a href=\"https://www.odoo.com/page/e-commerce\">Open Source eCommerce</a>\n"
"unlike anything you have ever seen before. Get an awesome catalog of products\n"
"and great product description pages.\n"
"\n"
"It's full-featured, integrated with your management software, fully\n"
"customizable and super easy.\n"
"\n"
"Create Awesome Product Pages\n"
"----------------------------\n"
"\n"
"Odoo's unique *'edit inline'* and building blocks approach makes product pages\n"
"creation surprisingly easy. \"Want to change the price of a product? or put it\n"
"in bold? Want to add a banner for a specific product?\" just click and change.\n"
"What you see is what you get. Really.\n"
"\n"
"Drag & Drop well designed *'Building Blocks'* to create beautifull product\n"
"pages that your customer will love.\n"
"\n"
"Increase Your Revenue Per Order\n"
"-------------------------------\n"
"\n"
"The built-in cross-selling feature helps you offer extra products related to\n"
"what the shopper put in his cart. (e.g. accessories)\n"
"\n"
"Odoo's upselling algorythm allows you to show visitors similar but more\n"
"expensive products than the one in view, with incentives.\n"
"\n"
"The inline editing feature allows you to easily change a price, launch a\n"
"promotion or fine tune the description of a product, in a just a click.\n"
"\n"
"A Clean Google Analytics Integration\n"
"------------------------------------\n"
"\n"
"Get a clear visibility of your sales funnel. Odoo's Google Analytics trackers\n"
"are configured by default to track all kind of events related to shopping\n"
"carts, call-to-actions, etc.\n"
"\n"
"As Odoo marketing tools (mass mailing, campaigns, etc) are also linked with\n"
"Google Analytics, you get a complete view of your business.\n"
"\n"
"Target New Markets\n"
"------------------\n"
"\n"
"Get your website translated in multiple languages with no effort. Odoo proposes\n"
"and propagates translations automatically across pages.\n"
"\n"
"Our translation \"on demand\" features allows you to benefit from professional\n"
"translators to translate all your changes automatically. Just change any part\n"
"of your website (a new blog post, a page modification, product descriptions,\n"
"...) and the translated versions are updated automatically in around 32 hours.\n"
"\n"
"Fine Tune Your Catalog\n"
"----------------------\n"
"\n"
"Get a full control on how you display your products in the catalog page:\n"
"promotional ribbons, related sized of products, discounts, variants, grid/list\n"
"view, etc.\n"
"\n"
"Edit any product inline to make your website evolve with your customer need.\n"
"\n"
"Acquire New Customers\n"
"---------------------\n"
"\n"
"SEO tools are ready to use, with no configuration required. Odoo suggests\n"
"keywords according to Google most searched terms, Google Analytics tracks your\n"
"shopping cart events, sitemap are created automatically for Google indexation,\n"
"etc.\n"
"\n"
"We even do structured content automatically to promote your product and events\n"
"efficiently in Google.\n"
"\n"
"Leverage Social Media\n"
"---------------------\n"
"\n"
"Create new landing pages easily with the Odoo inline edition feature. Send\n"
"visitors of your different marketing campaigns to specific landing pages to\n"
"optimize conversions.\n"
"\n"
"Manage a Reseller Network\n"
"-------------------------\n"
"\n"
"Manage a reseller network to target new market, have local presences or broaden\n"
"your distribution. Give them access to your reseller portal for an efficient\n"
"collaboration.\n"
"\n"
"Promote your resellers online, forward leads to resellers (with built-in\n"
"geolocalisation feature), define specific pricelists, launch a loyalty program\n"
"(offer specific discounts to your best customers or resellers), etc.\n"
"\n"
"Benefit from the power of Odoo, in your online store: a powerfull tax engine,\n"
"flexible pricing structures, a real inventory management solution, a reseller\n"
"interface, support for products with different behaviours; physical goods,\n"
"events, services, variants and options, etc.\n"
"\n"
"You don't need to interface with your warehouse, sales or accounting software.\n"
"Everything is integrated with Odoo. No pain, real time.\n"
"\n"
"A Clean Checkout Process\n"
"------------------------\n"
"\n"
"Convert most visitor interests into real orders with a clean checkout process\n"
"with a minimal number of steps and a great useability on every page.\n"
"\n"
"Customize your checkout process to fit your business needs: payment modes,\n"
"delivery methods, cross-selling, special conditions, etc.\n"
"\n"
"And much more...\n"
"----------------\n"
"\n"
"### Online Sales\n"
"\n"
"- Mobile Interface\n"
"- Sell products, events or services\n"
"- Flexible pricelists\n"
"- Product multi-variants\n"
"- Multiple stores\n"
"- Great checkout process\n"
"\n"
"### Customer Service\n"
"\n"
"- Customer Portal to track orders\n"
"- Assisted shopping with website live chats\n"
"- Returns management\n"
"- Advanced shipping rules\n"
"- Coupons or gift certificates\n"
"\n"
"### Order Management\n"
"\n"
"- Advanced warehouse management features\n"
"- Invoicing and accounting integration\n"
"- Mass mailing and customer segmentations\n"
"- Lead automation and marketing campaigns\n"
"- Persistent shopping cart\n"
"\n"
"Fully Integrated With Other Apps\n"
"--------------------------------\n"
"\n"
"### CMS\n"
"\n"
"Easily create awesome websites with no technical knowledge required.\n"
"\n"
"### Blogs\n"
"\n"
"Write news, attract new visitors, build customer loyalty.\n"
"\n"
"### Online Events\n"
"\n"
"Schedule, organize, promote or sell events online; conferences, webinars, trainings, etc.\n"
"\n"
#. module: base
#: model:ir.actions.act_window,help:base.action_partner_customer_form
@@ -19978,75 +19315,6 @@ msgid ""
"accurate reports on your team's performance.\n"
"\n"
msgstr ""
"Project Management\n"
"------------------\n"
"\n"
"### Infinitely flexible. Incredibly easy to use.\n"
"\n"
"\n"
"Odoo's collaborative and realtime <a href=\"https://www.odoo.com/page/project-management\">open source project management</a>\n"
"helps your team get work done. Keep track of everything, from the big picture\n"
"to the minute details, from the customer contract to the billing.\n"
"\n"
"Designed to fit your own process\n"
"--------------------------------\n"
"\n"
"Organize projects around your own processes. Work on tasks and issues using the\n"
"kanban view, schedule tasks using the gantt chart and control deadlines in the\n"
"calendar view. Every project may have it's own stages allowing teams to\n"
"optimize their job.\n"
"\n"
"Easy to use\n"
"-----------\n"
"\n"
"Get organized as fast as you can think. The easy-to-use interface takes no time\n"
"to learn, and every action is instantaneous, so there’s nothing standing\n"
"between you and your sweet productive flow.\n"
"\n"
"Work Together\n"
"-------------\n"
"\n"
"### Real time chats, document sharing, email integration\n"
"\n"
"Use open chatter to communicate with your team or customers and share comments\n"
"and documents on tasks and issues. Integrate discussion fast with the email\n"
"integration.\n"
"\n"
"Talk to others users or customers with the website live chat feature.\n"
"\n"
"Collaborative Writing\n"
"---------------------\n"
"\n"
"### The power of etherpad, inside your tasks\n"
"\n"
"Collaboratively edit the same specifications or meeting minutes right inside\n"
"the application. The incorporated etherpad feature allows several people to\n"
"work on the same tasks, at the same time.\n"
"\n"
"This is very efficient for scrum meetings, meeting minutes or complex\n"
"specifications. Every user has their own color and you can replay the whole\n"
"creation of the content.\n"
"\n"
"Get Work Done\n"
"-------------\n"
"\n"
"Get alerts on followed events to stay up to date with what interests you. Use\n"
"instant green/red visual indicators to scan through what has been done and what\n"
"requires your attention.\n"
"\n"
"Timesheets, Contracts & Invoicing\n"
"---------------------------------\n"
"\n"
"Projects are automatically integrated with customer contracts allowing you to\n"
"invoice based on time & materials and record timesheets easily.\n"
"\n"
"Track Issues\n"
"------------\n"
"\n"
"Single out the issues that arise in a project in order to have a better focus\n"
"on resolving them. Integrate customer interaction on every issue and get\n"
"accurate reports on your team's performance.\n"
"\n"
#. module: base
#: model:ir.module.module,summary:base.module_helpdesk_sale_timesheet
@@ -22830,8 +22098,8 @@ msgid ""
"tests independently to functional aspects of other models. "
msgstr ""
"Deze module bevat tests met betrekking tot e-mail. Die zijn\n"
"opgenomen in een afzonderlijke module omdat het modellen bevat die alleen worden gebruikt om te presteren\n"
"test onafhankelijk op functionele aspecten van andere modellen."
"opgenomen in een afzonderlijke module omdat het modellen bevat die alleen worden gebruikt om\n"
"test onafhankelijk uit te voeren op functionele aspecten van andere modellen."
#. module: base
#: model:ir.module.module,description:base.module_website_sale_taxcloud_delivery
+4 -4
View File
@@ -10595,7 +10595,7 @@ msgstr ""
#. module: base
#: model:ir.model.fields,field_description:base.field_res_lang__week_start
msgid "First Day of Week"
msgstr ""
msgstr "Prima zi a săptămânii"
#. module: base
#: model:ir.module.module,shortdesc:base.module_fleet
@@ -13908,7 +13908,7 @@ msgstr "Document(e) lipsă"
#: code:addons/models.py:5177
#, python-format
msgid "Missing required value for the field '%s' (%s)"
msgstr ""
msgstr "Lipsește valoarea necesară pentru câmp '%s' (%s)"
#. module: base
#: code:addons/base/models/ir_ui_view.py:386
@@ -17973,7 +17973,7 @@ msgstr "Caută Partener"
#. module: base
#: model:ir.model.fields,field_description:base.field_ir_actions_act_window__search_view
msgid "Search View"
msgstr ""
msgstr "Vizualizare Căutare"
#. module: base
#: model:ir.model.fields,field_description:base.field_ir_actions_act_window__search_view_id
@@ -18529,7 +18529,7 @@ msgstr "Termen Sursa"
#. module: base
#: model:ir.model.fields,field_description:base.field_ir_translation__source
msgid "Source term"
msgstr ""
msgstr "Termen Sursă"
#. module: base
#: model:res.country,name:base.za
+22 -21
View File
@@ -32,6 +32,7 @@
# Viktor Pogrebniak <vp@aifil.ru>, 2018
# Русский «kolobok2048» Ивашка <kolobok2048@gmail.com>, 2018
# Vasiliy Korobatov <korobatov@gmail.com>, 2019
# Oleg Kuryan <oleg@ventor.tech>, 2019
#
msgid ""
msgstr ""
@@ -39,7 +40,7 @@ msgstr ""
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-08-02 10:02+0000\n"
"PO-Revision-Date: 2016-08-05 12:54+0000\n"
"Last-Translator: Vasiliy Korobatov <korobatov@gmail.com>, 2019\n"
"Last-Translator: Oleg Kuryan <oleg@ventor.tech>, 2019\n"
"Language-Team: Russian (https://www.transifex.com/odoo/teams/41243/ru/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -12294,7 +12295,7 @@ msgstr ""
#. module: base
#: model:res.country,name:base.gf
msgid "French Guiana"
msgstr ""
msgstr "Французская Гвиана"
#. module: base
#: model:ir.module.module,shortdesc:base.module_l10n_fr_hr_payroll
@@ -12304,7 +12305,7 @@ msgstr "Французская Платежная ведомость"
#. module: base
#: model:res.country,name:base.pf
msgid "French Polynesia"
msgstr ""
msgstr "Французская Полинезия"
#. module: base
#: model:res.country,name:base.tf
@@ -12744,12 +12745,12 @@ msgstr "Группы (нет группы = все)"
#. module: base
#: model:res.country,name:base.gp
msgid "Guadeloupe"
msgstr ""
msgstr "Гваделупа"
#. module: base
#: model:res.country,name:base.gu
msgid "Guam"
msgstr ""
msgstr "Гуам"
#. module: base
#: model:res.country,name:base.gt
@@ -12774,7 +12775,7 @@ msgstr "Гвинея"
#. module: base
#: model:res.country,name:base.gw
msgid "Guinea-Bissau"
msgstr ""
msgstr "Гвинея-Бисау"
#. module: base
#: model:res.country,name:base.gy
@@ -12864,7 +12865,7 @@ msgstr ""
#. module: base
#: model:res.country,name:base.hm
msgid "Heard Island and McDonald Islands"
msgstr ""
msgstr "Остров Херд и острова Макдональд"
#. module: base
#: selection:ir.translation,type:0
@@ -14251,7 +14252,7 @@ msgstr "Кувейт"
#. module: base
#: model:res.country,name:base.kg
msgid "Kyrgyzstan"
msgstr ""
msgstr "Киргизия"
#. module: base
#: model:res.partner.industry,full_name:base.res_partner_industry_L
@@ -15222,7 +15223,7 @@ msgstr "Маршалловы Острова"
#. module: base
#: model:res.country,name:base.mq
msgid "Martinique"
msgstr ""
msgstr "Мартиника"
#. module: base
#: model:ir.module.module,shortdesc:base.module_mass_mailing_themes
@@ -15690,7 +15691,7 @@ msgstr ""
#. module: base
#: model:res.country,name:base.md
msgid "Moldova"
msgstr ""
msgstr "Молдавия"
#. module: base
#: model:res.country,name:base.mc
@@ -15866,7 +15867,7 @@ msgstr "Нидерланды - Бухгалтерские отчёты"
#. module: base
#: model:res.country,name:base.nc
msgid "New Caledonia"
msgstr ""
msgstr "Новая Каледония"
#. module: base
#: code:addons/base/wizard/base_export_language.py:18
@@ -18194,7 +18195,7 @@ msgstr "Телефон:"
#. module: base
#: model:res.country,name:base.pn
msgid "Pitcairn Islands"
msgstr ""
msgstr "Острова Питкэрн"
#. module: base
#: selection:ir.actions.act_window.view,view_mode:0
@@ -19270,7 +19271,7 @@ msgstr "Руанда"
#. module: base
#: model:res.country,name:base.re
msgid "Réunion"
msgstr ""
msgstr "Реюньон"
#. module: base
#: model:res.partner.industry,full_name:base.res_partner_industry_S
@@ -19335,12 +19336,12 @@ msgstr "Святой Варфоломей"
#. module: base
#: model:res.country,name:base.sh
msgid "Saint Helena, Ascension and Tristan da Cunha"
msgstr ""
msgstr "Острова Святой Елены, Вознесения и Тристан-да-Кунья"
#. module: base
#: model:res.country,name:base.kn
msgid "Saint Kitts and Nevis"
msgstr ""
msgstr "Сент-Китс и Невис"
#. module: base
#: model:res.country,name:base.lc
@@ -19360,7 +19361,7 @@ msgstr "Сен-Пьер и Микелон"
#. module: base
#: model:res.country,name:base.vc
msgid "Saint Vincent and the Grenadines"
msgstr ""
msgstr "Сент-Винсент и Гренадины"
#. module: base
#: model:ir.module.module,summary:base.module_hr_contract_salary
@@ -20251,7 +20252,7 @@ msgstr "Название Региона"
#. module: base
#: model:res.country,name:base.ps
msgid "State of Palestine"
msgstr ""
msgstr "Государство Палестина"
#. module: base
#: model:ir.model.fields,field_description:base.field_res_country__state_ids
@@ -20432,7 +20433,7 @@ msgstr "Опросы"
#. module: base
#: model:res.country,name:base.sj
msgid "Svalbard and Jan Mayen"
msgstr ""
msgstr "Шпицберген и Ян-Майен"
#. module: base
#: model:res.country,name:base.sz
@@ -20500,7 +20501,7 @@ msgstr "Обновление Системы"
#. module: base
#: model:res.country,name:base.st
msgid "São Tomé and Príncipe"
msgstr ""
msgstr "Сан-Томе и Принсипи"
#. module: base
#: model:res.partner.industry,full_name:base.res_partner_industry_T
@@ -21425,7 +21426,7 @@ msgstr "Смещение часового пояса"
#. module: base
#: model:res.country,name:base.tl
msgid "Timor-Leste"
msgstr ""
msgstr "Восточный Тимор"
#. module: base
#: model:ir.model.fields,field_description:base.field_res_partner__title
@@ -22557,7 +22558,7 @@ msgstr "Стоимость товара на складе WMS"
#. module: base
#: model:res.country,name:base.wf
msgid "Wallis and Futuna"
msgstr ""
msgstr "Уоллис и Футуна"
#. module: base
#: model:ir.module.category,name:base.module_category_warehouse
+286 -41
View File
@@ -4648,6 +4648,14 @@ msgid ""
"* Possibility to add a measure to the quality check with a min/max tolerance\n"
"* Define your stages for the quality alerts\n"
msgstr ""
"\n"
"База якості\n"
"===============\n"
"* Визначте пункти якості, які будуть генерувати перевірки якості на комплектуваннях,\n"
" замовлення на виробництво або робочі замовлення (quality_mrp)\n"
"* Сповіщення якості можуть створюватися незалежно або залежно від перевірок якості\n"
"* Можливість додати міру до перевірки якості з мін/макс дозволом\n"
"* Визначіть ваші етапи сповіщення якості\n"
#. module: base
#: model:ir.module.module,description:base.module_quality_control
@@ -4661,6 +4669,14 @@ msgid ""
"* Possibility to add a measure to the quality check with a min/max tolerance\n"
"* Define your stages for the quality alerts\n"
msgstr ""
"\n"
"Контроль якості\n"
"===============\n"
"* Визначте якісні показники, які будуть генерувати якість перевірок комплектування,\n"
"замовлення на виробництво або робоче замовлення (quality_mrp)\n"
"* Сповіщення про якість можуть бути створені незалежно або пов'язані з перевірками якості\n"
"* Можливість додати міру до перевірки якості з мінімальною/максимальною толерантністю\n"
"* Визначте свої етапи для попередження про якість\n"
#. module: base
#: model:ir.module.module,description:base.module_sale_expense
@@ -4893,6 +4909,16 @@ msgid ""
"\n"
" "
msgstr ""
"\n"
"Сингапурський план ріхунків та локалізація.\n"
"=======================================================\n"
"\n"
"Цей модуль включає, для обліку:\n"
" - План рахунків Сингапуру\n"
" - Поле UEN (Унікальний номер об'єкта) на компанії та партнері\n"
" - Поле PermitNo і PermitNoDate на рахунку-фактурі\n"
"\n"
" "
#. module: base
#: model:ir.module.module,description:base.module_l10n_es
@@ -5320,6 +5346,10 @@ msgid ""
"========================================================================\n"
" "
msgstr ""
"\n"
"Це базовий модуль для управління одиницями вимірювання.\n"
"========================================================================\n"
" "
#. module: base
#: model:ir.module.module,description:base.module_product
@@ -6033,6 +6063,18 @@ msgid ""
"- Check on bottom: ADP standard\n"
" "
msgstr ""
"\n"
"Цей модуль дозволяє друкувати ваші платежі на попередньо друкованих чеках.\n"
"Ви можете налаштувати вихід (компонування, заглушки, формат паперу тощо) у налаштуваннях компанії, а також керувати \n"
"нумерацією чеків (якщо ви використовуєте попередньо надруковані чеки без номерів) у налаштуваннях журналу.\n"
"За Канадською асоціацією платежів (https://www.payments.ca/sites/default/files/standard_006_complete_0.pdf)\n"
"\n"
"Підтримувані формати\n"
"-----------------\n"
"- Чек зверху : Quicken / QuickBooks standard\n"
"- Чек посередині: Peachtree standard\n"
"- Чек внизу: ADP standard\n"
" "
#. module: base
#: model:ir.module.module,description:base.module_anonymization
@@ -6619,6 +6661,9 @@ msgid ""
"Use budgets to compare actual with expected revenues and costs\n"
"--------------------------------------------------------------\n"
msgstr ""
"\n"
"Використовуйте бюджети для порівняння фактичних і очікуваних доходів і витрат\n"
"--------------------------------------------------------------\n"
#. module: base
#: model:ir.module.module,description:base.module_sales_team
@@ -6742,6 +6787,11 @@ msgid ""
"\n"
" "
msgstr ""
"\n"
"Перегляньте статистику посилань трекера на інформаційній панелі електронної комерції\n"
"=====================================================\n"
"\n"
" "
#. module: base
#: model:ir.module.module,description:base.module_stock_account
@@ -6957,6 +7007,7 @@ msgid ""
" This is the base module to manage chart of accounting and localization for "
"Hong Kong "
msgstr ""
"Це базовий модуль для управління планом рахунків та локалізації для Гонконгу"
#. module: base
#: model:ir.ui.view,arch_db:base.res_lang_form
@@ -8841,7 +8892,7 @@ msgstr "Аналітичний бухоблік"
#. module: base
#: model:ir.module.module,shortdesc:base.module_analytic_enterprise
msgid "Analytic Accounting Enterprise"
msgstr ""
msgstr "Аналітичний бухоблік Enterprise"
#. module: base
#: model:ir.module.module,summary:base.module_mrp_account
@@ -9411,7 +9462,7 @@ msgstr "Базове поле"
#: code:addons/base/models/res_lang.py:240
#, python-format
msgid "Base Language 'en_US' can not be deleted."
msgstr ""
msgstr "Базову мову 'en_US' не можна видалити."
#. module: base
#: selection:ir.model,state:0
@@ -9465,12 +9516,12 @@ msgstr "Базовий перегляд 2D Grid для odoo"
#. module: base
#: model:ir.module.module,summary:base.module_web_cohort
msgid "Basic Cohort view for odoo"
msgstr ""
msgstr "Базовий перегляд когорти для odoo"
#. module: base
#: model:ir.module.module,summary:base.module_quality
msgid "Basic Feature for Quality"
msgstr ""
msgstr "Базова функція для якості"
#. module: base
#: model:ir.module.module,summary:base.module_iap
@@ -9848,7 +9899,7 @@ msgstr "Канада - Бухгалтерський облік"
#. module: base
#: model:ir.module.module,shortdesc:base.module_l10n_ca_check_printing
msgid "Canadian Checks Layout"
msgstr ""
msgstr "Канадійський шаблон чеку"
#. module: base
#: model:ir.ui.view,arch_db:base.base_partner_merge_automatic_wizard_form
@@ -9892,13 +9943,14 @@ msgstr "Відмінити Оновлення"
#: code:addons/models.py:1981
#, python-format
msgid "Cannot aggregate field %r."
msgstr ""
msgstr "Неможливо агрегувати поле %r."
#. module: base
#: code:addons/base/models/res_lang.py:228
#, python-format
msgid "Cannot deactivate a language that is currently used by users."
msgstr ""
"Не вдається деактивувати мову, яка наразі використовується користувачами."
#. module: base
#: code:addons/base/models/res_config.py:385
@@ -10452,7 +10504,7 @@ msgstr "Порівняйте табелі та прогноз для ваших
#. module: base
#: model:ir.module.module,summary:base.module_project_timesheet_forecast
msgid "Compare timesheets and forecasts"
msgstr ""
msgstr "Порівняйте графіки та прогнози"
#. module: base
#: model:ir.model.fields,field_description:base.field_res_partner__contact_address
@@ -10715,7 +10767,7 @@ msgstr "Скопійовано"
#. module: base
#: model:ir.module.category,name:base.module_category_theme_corporate
msgid "Corporate"
msgstr ""
msgstr "Корпоративний"
#. module: base
#: model:res.country,name:base.cr
@@ -10855,7 +10907,7 @@ msgstr "Створіть банківський рахунок"
#. module: base
#: model:ir.actions.act_window,help:base.action_ui_view_custom
msgid "Create a customized view"
msgstr ""
msgstr "Створіть налаштований перегляд"
#. module: base
#: selection:ir.actions.server,state:0
@@ -10881,7 +10933,7 @@ msgstr "Створити новий тег для контактів"
#: model:ir.actions.act_window,help:base.action_partner_customer_form
#: model:ir.actions.act_window,help:base.action_partner_form
msgid "Create a new customer in your address book"
msgstr ""
msgstr "Створіть нового клієнта ваші адресній книзі"
#. module: base
#: model:ir.actions.act_window,help:base.action_attachment
@@ -10896,7 +10948,7 @@ msgstr "Створити нову індустрію"
#. module: base
#: model:ir.actions.act_window,help:base.action_partner_supplier_form
msgid "Create a new vendor in your address book"
msgstr ""
msgstr "Створіть нового постачальника у вашій адресній книзі"
#. module: base
#: model:ir.actions.act_window,help:base.action_res_company_form
@@ -12064,7 +12116,7 @@ msgstr "Легко створюйте та налаштовуйте ваші д
#. module: base
#: model:ir.module.module,shortdesc:base.module_delivery_easypost
msgid "Easypost Shipping"
msgstr ""
msgstr "Доставка Easypost"
#. module: base
#: model:res.country,name:base.ec
@@ -12099,6 +12151,9 @@ msgid ""
"partner is a customer without access or with a limited access created for "
"sharing data."
msgstr ""
"Або клієнт (не користувач), або спільний користувач. Вказаний поточний "
"партнер - клієнт без доступу або з обмеженим доступом, створеним для обміну "
"даними."
#. module: base
#: model:res.country,name:base.sv
@@ -12282,6 +12337,10 @@ msgid ""
"* Traceability report\n"
"* Cost Structure report (mrp_account)"
msgstr ""
"Розширення підприємства для MRP\n"
"* Планування робочого замовлення. Перевірте планування по перегляду Гантта, згрупованих за виробничим замовленням/робочим центром\n"
"* Звіт про відстеження\n"
"* Звіт про структуру витрат (mrp_account)"
#. module: base
#: model:res.partner.industry,name:base.res_partner_industry_R
@@ -13651,7 +13710,7 @@ msgstr "Німеччина"
#. module: base
#: model:ir.module.module,summary:base.module_website_sale_dashboard
msgid "Get a new dashboard view in the Website App"
msgstr ""
msgstr "Отримайте новий перегляд панелі приладів у додатку Веб-сайт "
#. module: base
#: model:ir.module.module,summary:base.module_website_sale_dashboard_with_margin
@@ -13687,6 +13746,9 @@ msgid ""
" Group-specific rules grant additional permissions, but are constrained within the bounds of global ones.\n"
" The first group rules restrict further the global rules, but can be relaxed by additional group rules."
msgstr ""
"Загальні правила (які не належать до групи) є обмеженнями, і їх не можна обійти.\n"
" Правила, специфічні для групи, надають додаткові дозволи, але обмежені в загальних межах.\n"
" Правила першої групи обмежують загальні правила, але можуть бути послаблені додатковими правилами групи."
#. module: base
#: model:ir.ui.view,arch_db:base.view_rule_form
@@ -13694,6 +13756,8 @@ msgid ""
"Global rules are combined together with a logical AND operator, and with the"
" result of the following steps"
msgstr ""
"Загальні правила об'єднуються разом з логічним оператором AND і з "
"результатом наступних кроків"
#. module: base
#: code:addons/base/models/res_config.py:734
@@ -13762,6 +13826,59 @@ msgid ""
"With this plugin, you can synchronize your Odoo calendar with Google Calendar.\n"
"\n"
msgstr ""
"Google Календар\n"
"---------------\n"
"\n"
"Отримуйте свої зустрічі, ваші відпустки... Отримайте свій календар у будь-якому\n"
" місці і ніколи не забувайте про подію, повністю синхронізовану з Odoo <a href=\"https://www.odoo.com/page/crm\">CRM з відкритим кодом</a>.\n"
"\n"
"Слідкуйте за своїми подіями\n"
"--------------------------\n"
"\n"
"Легко бачите мету зустрічі, час початку, а також учасника(ів) ... \n"
"Все це без натискання на що-небудь ...\n"
"\n"
"Подію створити так легко\n"
"-------------------------\n"
"\n"
"В один клік можна створити подію ... Ви можете перетягнути подію, якщо\n"
" ви хочете перенести її на інший час. Ви можете зменшити або\n"
" продовжити подію, якщо потрібно змінити години початку або\n"
" тривалість зустрічі.\n"
"\n"
"Створюйте повторювану подію\n"
"----------------------\n"
"\n"
"Ви також можете створювати повторювані події тільки з однією подією.\n"
" Вам потрібно створити подію кожного понеділка тижня? За допомогою\n"
" лише одного можна вказати повторення, а якщо ця подія переміщена\n"
" або вилучена, це не є проблемою, ви можете розв'язати подію від інших повторів.\n"
"\n"
"Переглядайте усі події, які ви хочете\n"
"------------------------\n"
"\n"
"Дивіться у своєму календарі, подію від інших людей, де ваш учасник, а\n"
" також їхні події, просто додавши вибраних колег. Кожен співробітник\n"
" матиме свій колір у вашому календарі, і кожен учасник матиме свій\n"
" аватар у події ...\n"
"\n"
"Отримуте електронну пошту\n"
"------------\n"
"\n"
"You will receive an email at creation of an event where you are attendee, but\n"
"also when this event is updated for some fields as date start, ...\n"
"\n"
"Be notified\n"
"-----------\n"
"\n"
"You can ask to have a alarm of type 'notification' in your Odoo. You will have\n"
"a notification in you Odoo which ever the page you are.\n"
"\n"
"Google Calendar\n"
"---------------\n"
"\n"
"With this plugin, you can synchronize your Odoo calendar with Google Calendar.\n"
"\n"
#. module: base
#: model:ir.module.module,shortdesc:base.module_google_drive
@@ -13871,6 +13988,7 @@ msgstr "Група контактів"
#: model:ir.ui.view,arch_db:base.view_rule_form
msgid "Group-specific rules are combined together with a logical OR operator"
msgstr ""
"Правила, специфічні для групи, об'єднуються разом з логічним оператором АБО"
#. module: base
#: model:ir.actions.act_window,name:base.action_res_groups
@@ -14097,7 +14215,7 @@ msgstr "Допомагає вам керувати табелями."
#. module: base
#: model:ir.module.category,description:base.module_category_user_type
msgid "Helps you manage users."
msgstr ""
msgstr "Допомагає вам керувати користувачами."
#. module: base
#: model:ir.module.category,description:base.module_category_event_management
@@ -14112,7 +14230,7 @@ msgstr "Допомагає вам керувати вашими оцінками
#. module: base
#: model:ir.module.category,description:base.module_category_hr_contract
msgid "Helps you manage your contracts."
msgstr ""
msgstr "Допомагає вам керувати вашими контрактами."
#. module: base
#: model:ir.module.category,description:base.module_category_hr_expense
@@ -14187,7 +14305,7 @@ msgstr ""
#. module: base
#: model:ir.module.category,description:base.module_category_website_sign
msgid "Helps you sign and complete your documents easily."
msgstr ""
msgstr "Допомагає вам підписувати та заповнювати ваші документи легко."
#. module: base
#: model:ir.ui.view,arch_db:base.wizard_lang_export
@@ -14237,7 +14355,7 @@ msgstr "Гонг Конг"
#. module: base
#: model:ir.module.module,shortdesc:base.module_l10n_hk
msgid "Hong Kong - Accounting"
msgstr ""
msgstr "Гонг Конг - Бухоблік"
#. module: base
#: model:ir.model.fields,help:base.field_ir_mail_server__smtp_host
@@ -14591,6 +14709,8 @@ msgid ""
"If user belongs to several groups, the results from step 2 are combined with"
" logical OR operator"
msgstr ""
"Якщо користувач належить до кількох груп, результати з кроку 2 поєднуються з"
" логічним оператором OR"
#. module: base
#: model:ir.actions.act_window,help:base.action_country_state
@@ -14922,7 +15042,7 @@ msgstr "Встановити мову"
#. module: base
#: model:ir.actions.server,name:base.action_server_module_immediate_install
msgid "Install Modules"
msgstr ""
msgstr "Встановити модулі"
#. module: base
#: selection:ir.module.module,state:0
@@ -15085,7 +15205,7 @@ msgstr "Недійсний параметр \"групувати за\""
#: code:addons/models.py:1983
#, python-format
msgid "Invalid aggregation function %r."
msgstr ""
msgstr "Недійсна функція агрегації %r."
#. module: base
#: code:addons/base/models/ir_fields.py:324
@@ -15113,7 +15233,7 @@ msgstr "Недійсне поле %s.%s"
#: code:addons/models.py:1973
#, python-format
msgid "Invalid field specification %r."
msgstr ""
msgstr "Недійсна специфікація поля %r."
#. module: base
#: sql_constraint:ir.ui.view:0
@@ -15153,7 +15273,7 @@ msgstr "Недійсний критерій пошуку"
#: code:addons/base/models/ir_ui_view.py:582
#, python-format
msgid "Invalid specification for moved nodes: '%s'"
msgstr ""
msgstr "Недійсна специфікація для переміщених вузлів: '%s'"
#. module: base
#: code:addons/base/models/ir_property.py:69
@@ -16131,17 +16251,17 @@ msgstr "Невдача доставки листа"
#. module: base
#: model:ir.module.module,shortdesc:base.module_mail_enterprise
msgid "Mail Enterprise"
msgstr ""
msgstr "Enterprise пошти"
#. module: base
#: model:ir.module.module,shortdesc:base.module_test_mail
msgid "Mail Tests"
msgstr ""
msgstr "Поштові тести"
#. module: base
#: model:ir.module.module,summary:base.module_test_mail
msgid "Mail Tests: performances and tests specific to mail"
msgstr ""
msgstr "Поштові тести: виступи та тести, характерні для пошти"
#. module: base
#: code:addons/base/models/ir_mail_server.py:486
@@ -16182,7 +16302,7 @@ msgstr "Обслуговування"
#. module: base
#: model:ir.module.module,shortdesc:base.module_hr_maintenance
msgid "Maintenance - HR"
msgstr ""
msgstr "Обслуговування - HR"
#. module: base
#: model:ir.module.module,shortdesc:base.module_mrp_maintenance
@@ -16422,7 +16542,7 @@ msgstr "Автоматичне відмічення"
#. module: base
#: model:ir.module.module,shortdesc:base.module_test_marketing_automation
msgid "Marketing Automation Tests"
msgstr ""
msgstr "Тести з автоматизації маркетингу"
#. module: base
#: model:ir.module.module,shortdesc:base.module_l10n_ma
@@ -16748,7 +16868,7 @@ msgstr "Модель %s не існує!"
#: code:addons/base/models/ir_model.py:180
#, python-format
msgid "Model '%s' contains module data and cannot be removed."
msgstr ""
msgstr "Модель '%s' вміщує дані модуля та не може бути видалена. "
#. module: base
#: model:ir.actions.act_window,name:base.action_model_constraint
@@ -17234,7 +17354,7 @@ msgstr "К-сть переглядів"
#. module: base
#: model:ir.actions.act_window,help:base.open_module_tree
msgid "No website module found!"
msgstr ""
msgstr "Не знайдено модуля веб-сайт!"
#. module: base
#: model:ir.model.fields,field_description:base.field_ir_model_data__noupdate
@@ -17262,6 +17382,11 @@ msgid ""
"supposed to do\n"
" "
msgstr ""
"Нетривіальні утиліти тестування можуть вимагати моделей і всього\n"
" \n"
"Цей модуль корисний для перевірки того, що вони роблять те, \n"
"що вони повинні робити\n"
" "
#. module: base
#: selection:ir.mail_server,smtp_encryption:0
@@ -17316,7 +17441,7 @@ msgstr "Не встановлений"
#: code:addons/base/models/ir_model.py:1395
#, python-format
msgid "Not enough access rights on the external ID:"
msgstr ""
msgstr "Недостатньо прав доступу для зовнішнього ID:"
#. module: base
#: model:ir.model.fields,field_description:base.field_res_partner__comment
@@ -19378,12 +19503,13 @@ msgstr ""
#: model:ir.actions.act_window,help:base.action_partner_customer_form
#: model:ir.actions.act_window,help:base.action_partner_form
msgid "Odoo helps you easily track all activities related to a customer."
msgstr ""
msgstr "Odoo допоможе вам легко відстежувати всі дії, пов'язані з клієнтом."
#. module: base
#: model:ir.actions.act_window,help:base.action_partner_supplier_form
msgid "Odoo helps you easily track all activities related to a vendor."
msgstr ""
"Odoo допоможе вам легко відстежувати всі дії, пов'язані з постачальником."
#. module: base
#: model:ir.model.fields,help:base.field_ir_sequence__padding
@@ -19959,6 +20085,121 @@ msgid ""
"\n"
"Create a great \"About us\" page by presenting your team efficiently.\n"
msgstr ""
"Організація заходів, тренінгів та веб-семінарів\n"
"-------------------------------------\n"
"\n"
"### Планування, просування, продаж, організація\n"
"\n"
"Організація, просування та продаж подій онлайн. Незалежно від того, чи організовуєте ви зустрічі, конференції, тренінги або вебінари, Odoo надає вам усі необхідні функції для керування подіями.\n"
"\n"
"Створіть чудові сторінки подій\n"
"--------------------------\n"
"\n"
"### Позбавтеся від старих редакторів WYSIWYG\n"
"\n"
"Створіть красиві сторінки подій за допомогою перетягування і розгортання добре створених *'Конструкторських блоків'*. Публікуйте фотографії подій, динаміки, розклад і т.д..\n"
"\n"
"Унікальний підхід Odoo до *\"вбудованого редагування\"* робить створення веб-сайту напрочуд легким. \"Хочете внести спікера? Змінити ціну квитка? Оновлювати банер? Сприяти спонсорам?\" просто натисніть і змініть.\n"
"\n"
"Продавайте квитки онлайн\n"
"-------------------\n"
"\n"
"### Automate the registration and payment process\n"
"\n"
"Sell registrations to your event with the multi-ticketing feature. Events can be free or for a fee. Attendees can pay online with a credit card or on invoice, based on your configuration.\n"
"\n"
"Boost your sales with early-bird prices, special conditions for members, or extra services with multiple tickets.\n"
"\n"
"A Clean Google Analytics Integration\n"
"------------------------------------\n"
"\n"
"### Control your sales funnel with Google Analytics\n"
"\n"
"Get a clear visibility of your sales funnel. Odoo's Google Analytics trackers are configured by default to track all kind of events related to shopping carts, call-to-actions, etc.\n"
"\n"
"As Odoo marketing tools (mass mailing, campaigns, etc) are also linked with Google Analytics, you get a full view of your business.\n"
"\n"
"Promote Events Efficiently\n"
"--------------------------\n"
"\n"
"### Mass Mailing & Social Media\n"
"\n"
"Use the segmentation, the social network integration and mass mailing features to promote your events to the right audience. Setup automated emails to attendees to send them last minute details.\n"
"\n"
"Designer-Friendly Themes\n"
"------------------------\n"
"\n"
"### Designers love working on Odoo\n"
"\n"
"Themes are awesome and easy to design. You don't need to develop to create new pages, themes or building blocks. We use a clean HTML structure, a [bootstrap](http://getbootstrap.com/) CSS and our modularity allows to distribute your themes easily.\n"
"\n"
"The building block approach allows the website to stay clean after the end-users start creating new contents.\n"
"\n"
"Make Your Event More Visible\n"
"----------------------------\n"
"\n"
"### SEO tools at your finger tips\n"
"\n"
"SEO tools are ready to use, with no configuration required. Odoo suggests keywords according to Google most searched terms, Google Analytics tracks your shopping cart events and sitemap are created automatically.\n"
"\n"
"We even do structured content automatically to promote your events and products efficiently in Google.\n"
"\n"
"Leverage Social Media\n"
"---------------------\n"
"\n"
"### Optimize: from Ads to Conversions\n"
"\n"
"Create new landing pages easily with the Odoo inline edition feature. Send visitors of your different marketing campaigns to event landing pages to optimize conversions.\n"
"\n"
"And Much More...\n"
"----------------\n"
"\n"
"### Schedule\n"
"\n"
"- Calendar of Events\n"
"- Publish related documents\n"
"- Ressources allocation\n"
"- Automate purchases (catering...)\n"
"- Multiple locations and organizers\n"
"- Mobile Interface\n"
"\n"
"### Sell\n"
"\n"
"- Online or offline sales\n"
"- Automated invoicing\n"
"- Cancellation policies\n"
"- Specific prices for members\n"
"- Dashboards and reporting\n"
"\n"
"### Organize\n"
"\n"
"- Advanced Planification\n"
"- Print Badges\n"
"- Automate Follow-up Emails\n"
"- Min/Max capacities\n"
"- Manage classes and ressources\n"
"- Create group of attendees\n"
"- Automate statisfaction surveys\n"
"\n"
"Fully Integrated With Others Apps\n"
"---------------------------------\n"
"\n"
"### Get hundreds of open source apps for free\n"
"\n"
"\n"
"### eCommerce\n"
"\n"
"Promote products, sell online, optimize visitors' shopping experiences.\n"
"\n"
"\n"
"### Blogs\n"
"\n"
"Write news, attract new visitors, build customer loyalty.\n"
"\n"
"\n"
"### Our Team\n"
"\n"
"Create a great \"About us\" page by presenting your team efficiently.\n"
#. module: base
#: model:ir.model.fields,field_description:base.field_report_paperformat__orientation
@@ -20036,7 +20277,7 @@ msgstr "DPI на виході"
#: code:addons/models.py:1995
#, python-format
msgid "Output name %r is used twice."
msgstr ""
msgstr "Назва виходу %r використовується двічі ."
#. module: base
#: model:ir.model.fields,field_description:base.field_base_language_import__overwrite
@@ -20424,6 +20665,9 @@ msgid ""
"Please configure an email on the current user to simulate sending an email "
"message via this outgoing server"
msgstr ""
"Будь ласка, налаштуйте електронний лист на поточного користувача, щоб "
"імітувати надсилання повідомлення електронної пошти через цей вихідний "
"сервер"
#. module: base
#: code:addons/base/models/ir_model.py:1212
@@ -20584,7 +20828,7 @@ msgstr "Округлення ціни"
#. module: base
#: model:ir.module.module,summary:base.module_l10n_ca_check_printing
msgid "Print CA Checks"
msgstr ""
msgstr "Друк чеків CA "
#. module: base
#: model:ir.module.module,shortdesc:base.module_print
@@ -20686,7 +20930,7 @@ msgstr "Шаблон електронної пошти отвару"
#. module: base
#: model:ir.module.module,shortdesc:base.module_mrp_bom_cost
msgid "Product Extension: Compute cost from BoM"
msgstr ""
msgstr "Розширення товару: обчислюйте витрати зі специфікації"
#. module: base
#: model:ir.module.module,shortdesc:base.module_mrp_plm
@@ -20997,12 +21241,12 @@ msgstr "Замовлення на купівлю, надходження, рах
#. module: base
#: model:ir.module.module,summary:base.module_purchase_stock
msgid "Purchase Orders, Receipts, Vendor Bills for Stock"
msgstr ""
msgstr "Замовлення на купівлю, надходження, рахунки постачальника для складу"
#. module: base
#: model:ir.module.module,shortdesc:base.module_purchase_stock
msgid "Purchase Stock"
msgstr ""
msgstr "Придбати запас"
#. module: base
#: model:ir.module.module,shortdesc:base.module_purchase_mrp
@@ -21060,7 +21304,7 @@ msgstr "Попередження якості та контрольні пунк
#. module: base
#: model:ir.module.module,shortdesc:base.module_quality
msgid "Quality Base"
msgstr ""
msgstr "База якості"
#. module: base
#: model:ir.module.module,shortdesc:base.module_quality_control
@@ -21083,6 +21327,7 @@ msgstr "Питання щодо Подій"
#: model:ir.module.module,summary:base.module_web_settings_dashboard
msgid "Quick actions for installing new app, adding users, etc."
msgstr ""
"Швидкі дії для встановлення нової програми, додавання користувачів тощо"
#. module: base
#: model:ir.module.module,summary:base.module_sale_expense
@@ -21262,7 +21507,7 @@ msgstr "Пов'язані дії сервера"
#. module: base
#: model:ir.model.fields,field_description:base.field_ir_model_fields__related_field_id
msgid "Related field"
msgstr ""
msgstr "Пов'язане поле"
#. module: base
#: code:addons/base/models/ir_model.py:425
@@ -22050,7 +22295,7 @@ msgstr "Надсилайте текстові повідомлення як на
#. module: base
#: model:ir.module.module,description:base.module_delivery_easypost
msgid "Send your parcels through Easypost and track them online"
msgstr ""
msgstr "Надсилайте свої посилки через Easypost і відстежуйте їх онлайн"
#. module: base
#: model:ir.module.module,description:base.module_delivery_dhl
@@ -22827,7 +23072,7 @@ msgstr "Архів TGZ"
#. module: base
#: model:ir.ui.view,arch_db:base.wizard_lang_export
msgid "TGZ format: bundles multiple PO(T) files as a single archive"
msgstr ""
msgstr "Формат TGZ: розшаровує декілька файлів PO(T) як єдиний архів"
#. module: base
#: model:ir.model.fields,field_description:base.field_res_company__vat
@@ -22981,12 +23226,12 @@ msgstr "Тестування продуктивності"
#. module: base
#: model:ir.module.module,summary:base.module_test_marketing_automation
msgid "Test Suits for Automated Marketing Campaigns"
msgstr ""
msgstr "Тестові костюми для автоматизованих маркетингових кампаній"
#. module: base
#: model:ir.module.module,shortdesc:base.module_test_testing_utilities
msgid "Test testing utilities"
msgstr ""
msgstr "Утиліти тестового тестування"
#. module: base
#: model:ir.module.module,description:base.module_test_access_rights
+1 -1
View File
@@ -438,7 +438,7 @@ class Partner(models.Model):
as if they were related fields """
commercial_partner = self.commercial_partner_id
if commercial_partner != self:
sync_vals = commercial_partner._update_fields_values(self._commercial_fields())
sync_vals = commercial_partner.with_prefetch()._update_fields_values(self._commercial_fields())
self.write(sync_vals)
@api.multi
+1 -1
View File
@@ -2472,7 +2472,7 @@ class One2many(_RelationalMulti):
to_relink = {} # lines to relink {line_id: record_id}
def unlink(line_ids):
if comodel._fields[inverse].ondelete == 'cascade':
if getattr(comodel._fields[inverse], 'ondelete', False) == 'cascade':
to_delete.extend(line_ids)
else:
to_relink.update(dict.fromkeys(line_ids, False))