diff --git a/addons/account/i18n/ca.po b/addons/account/i18n/ca.po index 68da51122b5..71951921291 100644 --- a/addons/account/i18n/ca.po +++ b/addons/account/i18n/ca.po @@ -11,6 +11,7 @@ # Marc Tormo i Bochaca , 2019 # RGB Consulting , 2019 # Quim - eccit , 2019 +# Sabina Batlle , 2019 # msgid "" msgstr "" @@ -18,7 +19,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: Quim - eccit , 2019\n" +"Last-Translator: Sabina Batlle , 2019\n" "Language-Team: Catalan (https://www.transifex.com/odoo/teams/41243/ca/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -216,7 +217,7 @@ msgstr "" #. module: account #: model_terms:ir.ui.view,arch_db:account.portal_my_home_menu_invoice msgid "Draft Invoice" -msgstr "" +msgstr "Factura esborrany" #. module: account #: model_terms:ir.ui.view,arch_db:account.account_move_line_view_kanban @@ -12070,13 +12071,6 @@ msgstr "" msgid "have been reconciled automatically." msgstr "" -#. module: account -#. openerp-web -#: code:addons/account/static/src/xml/account_reconciliation.xml:44 -#, python-format -msgid "o_manual_statement" -msgstr "" - #. module: account #: selection:account.payment.term.line,option:0 msgid "of the current month" diff --git a/addons/account/i18n/es.po b/addons/account/i18n/es.po index be0a06a0e79..91bec89e08e 100644 --- a/addons/account/i18n/es.po +++ b/addons/account/i18n/es.po @@ -70,9 +70,9 @@ # Miquel Torner , 2019 # Nicolás Broggi , 2019 # Luis González , 2019 -# Gabriel Umaña , 2019 # Luis Marin , 2019 # Edilianny Sánchez , 2019 +# Gabriel Umana , 2019 # msgid "" msgstr "" @@ -80,7 +80,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: Edilianny Sánchez , 2019\n" +"Last-Translator: Gabriel Umana , 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" @@ -5107,7 +5107,7 @@ msgstr "Agrupar líneas de factura" #. module: account #: model:ir.model.fields,field_description:account.field_account_register_payments__group_invoices msgid "Group Invoices" -msgstr "" +msgstr "Grupo de facturas" #. module: account #: selection:account.tax,amount_type:0 @@ -12358,13 +12358,6 @@ msgstr "" msgid "have been reconciled automatically." msgstr "" -#. module: account -#. openerp-web -#: code:addons/account/static/src/xml/account_reconciliation.xml:44 -#, python-format -msgid "o_manual_statement" -msgstr "o_manual_statement" - #. module: account #: selection:account.payment.term.line,option:0 msgid "of the current month" diff --git a/addons/account/i18n/fr.po b/addons/account/i18n/fr.po index 1288fa09eb5..6ea1c304c29 100644 --- a/addons/account/i18n/fr.po +++ b/addons/account/i18n/fr.po @@ -41,7 +41,6 @@ # Fabri Yohann , 2017 # FR R4 , 2017 # Matillon Samantha , 2017 -# Moka Tourisme , 2018 # Olivier Dony , 2018 # Hubert TETARD , 2018 # Nancy Bolognesi , 2018 @@ -79,6 +78,8 @@ # Alain van de Werve , 2019 # Khalyomede Khalyomede , 2019 # Fabien Bourgeois , 2019 +# Phil Anth , 2019 +# Moka Tourisme , 2019 # msgid "" msgstr "" @@ -86,7 +87,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: Fabien Bourgeois , 2019\n" +"Last-Translator: Moka Tourisme , 2019\n" "Language-Team: French (https://www.transifex.com/odoo/teams/41243/fr/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -681,7 +682,7 @@ msgstr "" #. module: account #: model_terms:ir.ui.view,arch_db:account.portal_invoice_page msgid "Your Contact" -msgstr "" +msgstr "Votre contact" #. module: account #: model_terms:ir.ui.view,arch_db:account.report_invoice_document_with_payments @@ -4909,6 +4910,8 @@ msgstr "Pour être en pourcentage, saisir une valeur entre 0 et 100." msgid "" "Forbidden unit price, account and quantity on non-accountable invoice line" msgstr "" +"Prix ​​unitaire, compte et quantité interdits sur la ligne de facture non " +"comptable" #. module: account #: model:ir.model.fields,help:account.field_account_reconcile_model__force_second_tax_included @@ -12422,13 +12425,6 @@ msgstr "" msgid "have been reconciled automatically." msgstr "" -#. module: account -#. openerp-web -#: code:addons/account/static/src/xml/account_reconciliation.xml:44 -#, python-format -msgid "o_manual_statement" -msgstr "o_manual_statement" - #. module: account #: selection:account.payment.term.line,option:0 msgid "of the current month" diff --git a/addons/account/i18n/hu.po b/addons/account/i18n/hu.po index f22a0acac83..71970867f2a 100644 --- a/addons/account/i18n/hu.po +++ b/addons/account/i18n/hu.po @@ -6,9 +6,9 @@ # picibucor , 2017 # Tibor Kőnig , 2019 # krnkris, 2019 -# gezza , 2019 # Kovács Tibor , 2019 # Martin Trigaux, 2019 +# gezza , 2019 # Ákos Nagy , 2019 # msgid "" @@ -4044,7 +4044,7 @@ msgstr "" #. module: account #: model:ir.model,name:account.model_digest_digest msgid "Digest" -msgstr "" +msgstr "Kivonat" #. module: account #: model:ir.model.fields,help:account.field_res_config_settings__group_products_in_bills @@ -4152,7 +4152,7 @@ msgstr "Feltüntetve a számlákon" #: code:addons/account/models/digest.py:16 #, python-format msgid "Do not have access, skip this data for user's digest email" -msgstr "" +msgstr "Nincs hozzáférés, adat kihagyása a felhasználó kivonat e-mailjéből" #. module: account #: model:ir.model.fields,help:account.field_account_invoice_send__auto_delete_message @@ -6332,7 +6332,7 @@ msgstr "" #: model_terms:ir.ui.view,arch_db:account.view_account_invoice_filter #: model_terms:ir.ui.view,arch_db:account.view_account_payment_search msgid "Late Activities" -msgstr "" +msgstr "Késő tevékenységek" #. module: account #: model:ir.model.fields,field_description:account.field_account_account__last_time_entries_checked @@ -6344,7 +6344,7 @@ msgstr "Legutóbbi Számlák & Fizetések könyvelői párosításának dátuma" #. module: account #: model:ir.model.fields,field_description:account.field_account_invoice_send__layout msgid "Layout" -msgstr "" +msgstr "Elrendezés" #. module: account #: model_terms:ir.ui.view,arch_db:account.view_account_position_form @@ -9344,6 +9344,10 @@ msgid "" "Today: Activity date is today\n" "Planned: Future activities." msgstr "" +"Tevékenységeken alapuló állapot\n" +"Lejárt: Az esedékesség dátuma már elmúlt\n" +"Ma: A tevékenység dátuma ma van\n" +"Tervezett: Jövőbeli tevékenységek." #. module: account #: model_terms:ir.ui.view,arch_db:account.onboarding_bank_account_step diff --git a/addons/account/i18n/mn.po b/addons/account/i18n/mn.po index b6ced1cabae..617e63fbdca 100644 --- a/addons/account/i18n/mn.po +++ b/addons/account/i18n/mn.po @@ -12,12 +12,12 @@ # Batzul B , 2018 # nasaaskii , 2019 # Chinzorita , 2019 -# Baskhuu Lodoikhuu , 2019 # Otgonbayar.A , 2019 # Ganbaatar Buriad , 2019 # Onii Onii , 2019 # Martin Trigaux, 2019 # Khishigbat Ganbold , 2019 +# Baskhuu Lodoikhuu , 2019 # msgid "" msgstr "" @@ -25,7 +25,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: Khishigbat Ganbold , 2019\n" +"Last-Translator: Baskhuu Lodoikhuu , 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" @@ -1116,7 +1116,7 @@ msgstr "Санхүү" #. module: account #: model_terms:ir.ui.view,arch_db:account.view_account_journal_form msgid "Accounting App Options" -msgstr "" +msgstr "Санхүү бүртгэлийн сонголтууд" #. module: account #: model:ir.model.fields,field_description:account.field_account_bank_statement__accounting_date @@ -1139,7 +1139,7 @@ msgstr "Санхүүгийн бичилт" #. module: account #: model:ir.actions.act_window,name:account.open_account_journal_dashboard_kanban msgid "Accounting Overview" -msgstr "" +msgstr "Санхүүгийн тойм" #. module: account #: model_terms:ir.ui.view,arch_db:account.view_partner_property_form @@ -2318,7 +2318,7 @@ msgstr "Төлөх тооцоо" #. module: account #: model_terms:ir.ui.view,arch_db:account.account_journal_dashboard_kanban_view msgid "Bills to Validate" -msgstr "" +msgstr "Батлах нэхэмжлэлүүд" #. module: account #: code:addons/account/models/account_journal_dashboard.py:178 @@ -2344,7 +2344,7 @@ msgstr "Байгаа улсуудыг харах" #. module: account #: model:ir.model.fields,field_description:account.field_res_config_settings__module_account_budget msgid "Budget Management" -msgstr "" +msgstr "Төсвийн удирдлага" #. module: account #: model:ir.model.fields,field_description:account.field_account_reconcile_model_template__name @@ -2359,7 +2359,7 @@ msgstr "Улсаар" #. module: account #: model:ir.filters,name:account.filter_invoice_refund msgid "By Credit Note" -msgstr "" +msgstr "Буцаалтын нэхэмжлэлээр" #. module: account #: model:ir.filters,name:account.filter_invoice_product @@ -2900,6 +2900,8 @@ msgstr "Мөнгө/Тооцооны Утга" #: model_terms:ir.ui.view,arch_db:account.res_config_settings_view_form msgid "Collect customer payments in one-click using Euro SEPA Service" msgstr "" +"Euro SEPA үйлчилгээ ашиглан ганцхан товч даралтаар харилцагчдын төлбөр " +"төлөлтүүдийг цуглуулна" #. module: account #: model_terms:ir.ui.view,arch_db:account.res_config_settings_view_form @@ -3466,7 +3468,7 @@ msgstr "Зээлийн карт" #. module: account #: model:ir.model.fields,field_description:account.field_account_move_line__credit_cash_basis msgid "Credit Cash Basis" -msgstr "" +msgstr "Мөнгөн суурьт кредит" #. module: account #: model:ir.model.fields,field_description:account.field_account_invoice_refund__filter_refund @@ -3476,7 +3478,7 @@ msgstr "" #. module: account #: model:ir.model.fields,field_description:account.field_account_partial_reconcile__credit_move_id msgid "Credit Move" -msgstr "" +msgstr "Кредит гүйлгээ" #. module: account #: code:addons/account/models/account_invoice.py:498 @@ -3494,22 +3496,22 @@ msgstr "Буцаалт" #: code:addons/account/models/account_invoice.py:499 #, python-format msgid "Credit Note - %s" -msgstr "" +msgstr "Буцаалт - %s" #. module: account #: model_terms:ir.ui.view,arch_db:account.account_journal_dashboard_kanban_view msgid "Credit Note Bill" -msgstr "" +msgstr "Өглөгийн буцаалт" #. module: account #: model:ir.model.fields,field_description:account.field_account_invoice_refund__date_invoice msgid "Credit Note Date" -msgstr "" +msgstr "Буцаалтын огноо" #. module: account #: model:ir.model.fields,field_description:account.field_account_journal__refund_sequence_id msgid "Credit Note Entry Sequence" -msgstr "" +msgstr "Буцаалтын гүйлгээний дугаарлалт" #. module: account #: model:ir.actions.act_window,name:account.action_invoice_out_refund @@ -3517,12 +3519,12 @@ msgstr "" #: model_terms:ir.ui.view,arch_db:account.account_journal_dashboard_kanban_view #: model_terms:ir.ui.view,arch_db:account.view_account_invoice_report_search msgid "Credit Notes" -msgstr "" +msgstr "Буцаалтууд" #. module: account #: model:ir.model.fields,field_description:account.field_account_journal__refund_sequence_number_next msgid "Credit Notes: Next Number" -msgstr "" +msgstr "Буцаалт: Дараагийн дугаар" #. module: account #: model:ir.model.fields,field_description:account.field_tax_adjustments_wizard__credit_account_id @@ -3645,7 +3647,7 @@ msgstr "Захиалагчийн Төлбөрийн нөхцлүүд" #: model_terms:ir.ui.view,arch_db:account.res_config_settings_view_form #: model_terms:ir.ui.view,arch_db:account.view_account_payment_search msgid "Customer Payments" -msgstr "" +msgstr "Харилцагчийн төлөлт" #. module: account #: model:ir.model.fields,help:account.field_account_invoice__access_url @@ -4460,7 +4462,7 @@ msgstr "Гадаад Код" #: code:addons/account/static/src/xml/account_reconciliation.xml:335 #, python-format msgid "External link" -msgstr "" +msgstr "Гадаад холбоос" #. module: account #: model:account.incoterms,name:account.incoterm_FAS @@ -4501,7 +4503,7 @@ msgstr "Холбооны Мужууд" #. module: account #: model:ir.model.fields,field_description:account.field_account_invoice_import_wizard__attachment_ids msgid "Files" -msgstr "" +msgstr "Файл" #. module: account #: model_terms:ir.ui.view,arch_db:account.cash_box_in_form @@ -4529,7 +4531,7 @@ msgstr "Санхүүгийн жилийн мэдээлэл" #. module: account #: model_terms:ir.ui.view,arch_db:account.res_config_settings_view_form msgid "Fiscal Localization" -msgstr "" +msgstr "Байршилаас хамаарах санхүү бүртгэл " #. module: account #: model:ir.model.fields,field_description:account.field_account_fiscal_position_account_template__position_id @@ -4539,7 +4541,7 @@ msgstr "Санхүүгийн зурагжуулалт" #. module: account #: model_terms:ir.ui.view,arch_db:account.res_config_settings_view_form msgid "Fiscal Periods" -msgstr "" +msgstr "Тайлант мөчлөг" #. module: account #: model:ir.actions.act_window,name:account.action_account_fiscal_position_template_form @@ -4583,35 +4585,35 @@ msgstr "Санхүүгийн жил" #. module: account #: model_terms:ir.ui.view,arch_db:account.action_account_fiscal_year_form msgid "Fiscal Year 2018" -msgstr "" +msgstr "Санхүүгийн жил 2018" #. module: account #: model:ir.actions.act_window,name:account.actions_account_fiscal_year #: model:ir.model.fields,field_description:account.field_res_config_settings__group_fiscal_year msgid "Fiscal Years" -msgstr "" +msgstr "Санхүүгийн жил" #. module: account #: model:ir.model.fields,help:account.field_account_financial_year_op__fiscalyear_last_day msgid "Fiscal year last day." -msgstr "" +msgstr "Санхүүгийн жилийн сүүлийн өдөр." #. module: account #: model:ir.model.fields,help:account.field_account_financial_year_op__fiscalyear_last_month msgid "Fiscal year last month." -msgstr "" +msgstr "Санхүүгийн жилийн сүүлийн сар." #. module: account #: model:ir.model.fields,field_description:account.field_account_financial_year_op__fiscalyear_last_day #: model:ir.model.fields,field_description:account.field_res_company__fiscalyear_last_day msgid "Fiscalyear Last Day" -msgstr "" +msgstr "Санхүүгийн жилийн сүүлийн өдөр" #. module: account #: model:ir.model.fields,field_description:account.field_account_financial_year_op__fiscalyear_last_month #: model:ir.model.fields,field_description:account.field_res_company__fiscalyear_last_month msgid "Fiscalyear Last Month" -msgstr "" +msgstr "Санхүүгийн жилийн сүүлийн сар" #. module: account #: selection:account.reconcile.model,amount_type:0 @@ -4672,12 +4674,12 @@ msgstr "Дагагчид (Харилцагчид)" #. module: account #: model:ir.model.fields,field_description:account.field_account_journal__inbound_payment_method_ids msgid "For Incoming Payments" -msgstr "" +msgstr "Ирж буй төлбөрийн хувьд" #. module: account #: model:ir.model.fields,field_description:account.field_account_journal__outbound_payment_method_ids msgid "For Outgoing Payments" -msgstr "" +msgstr "Гарч буй төлбөрийн хувьд" #. module: account #: model:ir.model.fields,help:account.field_account_payment_term_line__value_amount @@ -4689,18 +4691,20 @@ msgstr "Хувь оруулахдаа 0-100 хооронд харьцааг ор msgid "" "Forbidden unit price, account and quantity on non-accountable invoice line" msgstr "" +"Санхүүгийн гүйлгээнд хамаарахгүй мөр дээр ямар нэг нэгж үнэ, данс, тоо " +"хэмжээний утга оруулахыг хориглоно" #. module: account #: model:ir.model.fields,help:account.field_account_reconcile_model__force_second_tax_included #: model:ir.model.fields,help:account.field_account_reconcile_model_template__force_second_tax_included msgid "Force the second tax to be managed as a price included tax." -msgstr "" +msgstr "Хоёрдогч нэмэлт татвар нь үнийн дүнд шингэсэн байдлаар хэрэгжинэ." #. module: account #: model:ir.model.fields,help:account.field_account_reconcile_model__force_tax_included #: model:ir.model.fields,help:account.field_account_reconcile_model_template__force_tax_included msgid "Force the tax to be managed as a price included tax." -msgstr "" +msgstr "Хоёрдогч нэмэлт татвар нь үнийн дүнд шингэсэн байдлаар хэрэгжинэ." #. module: account #: model:ir.model.fields,help:account.field_account_account__currency_id @@ -4760,7 +4764,7 @@ msgstr "" #: code:addons/account/models/account_invoice.py:387 #, python-format msgid "From: " -msgstr "" +msgstr "Хаанаас:" #. module: account #: model:ir.model,name:account.model_account_full_reconcile @@ -4794,12 +4798,12 @@ msgstr "Бичилтүүдийг үүсгэх" #. module: account #: model:ir.ui.menu,name:account.account_reports_legal_statements_menu msgid "Generic Statements" -msgstr "" +msgstr "Нийтлэг тайлангууд" #. module: account #: model_terms:ir.ui.view,arch_db:account.res_config_settings_view_form msgid "Get warnings when invoicing specific customers" -msgstr "" +msgstr "Зарим онцгой харилцагчид нэхэмжлэл бүртгэх үед анхааруулга өгөх" #. module: account #: model_terms:ir.ui.view,arch_db:account.res_config_settings_view_form @@ -4809,6 +4813,11 @@ msgid "" "“Bank Synchronization” in bank account settings. Then, click “Configure” on " "the online account to enter your bank credentials." msgstr "" +"Yodlee ба Plaid үйлчилгээг ашигласнаар Таны банкны гүйлгээ 4 цаг тутамд " +"автоматаар бүртгэгдэх эсвэл товч дарж тэр даруй бүртгэгдэх боломжтой. Энэ " +"нэмэлтийг суулгасны дараа банкны дансны тохиргооны хэсэг дэх \"Банк " +"ижилсүүлэлт\" талбарт \"Банкнаас татах\" утгыг сонгоно. Дараа нь " +"\"Тохируулах\" товч дээр дарж онлайн дансны бүртгэлээ оруулна. " #. module: account #: model:ir.model.fields,help:account.field_account_invoice_line__sequence @@ -4830,14 +4839,14 @@ msgstr "Нэхэмжлэлийн татварын харагдах эрэмбэ #: model:ir.model.fields,help:account.field_account_payment_term_line__sequence msgid "" "Gives the sequence order when displaying a list of payment terms lines." -msgstr "" +msgstr "Төлбөрийн нөхцөлийн мөрүүдийн хэрэгжих эрэмбэ, дарааллыг тодорхойлно." #. module: account #. openerp-web #: code:addons/account/static/src/xml/account_reconciliation.xml:63 #, python-format msgid "Go to bank statement(s)" -msgstr "" +msgstr "Банкны бүртгэл рүү очно уу" #. module: account #: code:addons/account/models/account_invoice.py:773 @@ -4850,7 +4859,7 @@ msgstr "Тохиргооны самбар руу очно уу" #: code:addons/account/models/company.py:482 #, python-format msgid "Go to the journal configuration" -msgstr "" +msgstr "Журнал тохируулга руу очих" #. module: account #: selection:res.partner,trust:0 @@ -4891,7 +4900,7 @@ msgstr "Нэхэмжлэлийн мөрүүдийг бүлэглэх" #. module: account #: model:ir.model.fields,field_description:account.field_account_register_payments__group_invoices msgid "Group Invoices" -msgstr "" +msgstr "Нэхэмжлэл бүлэглэлт" #. module: account #: selection:account.tax,amount_type:0 @@ -4902,22 +4911,22 @@ msgstr "Татварын бүлэг" #. module: account #: model_terms:ir.ui.view,arch_db:account.res_config_settings_view_form msgid "Group payments into a single batch to ease the reconciliation process" -msgstr "" +msgstr "Хялбар тулгалт хийх зорилгоор төлөлтүүдийг бүлэглэж багц болгох" #. module: account #: selection:account.cash.rounding,rounding_method:0 msgid "HALF-UP" -msgstr "" +msgstr "HALF-UP" #. module: account #: model:ir.model.fields,field_description:account.field_res_config_settings__has_accounting_entries msgid "Has Accounting Entries" -msgstr "" +msgstr "Ажил гүйлгээтэй" #. module: account #: model:ir.model.fields,field_description:account.field_account_payment__has_invoices msgid "Has Invoices" -msgstr "" +msgstr "Нэхэмжлэлтэй" #. module: account #: model:ir.model.fields,field_description:account.field_account_invoice__has_outstanding @@ -4928,30 +4937,30 @@ msgstr "" #: model:ir.model.fields,field_description:account.field_res_partner__has_unreconciled_entries #: model:ir.model.fields,field_description:account.field_res_users__has_unreconciled_entries msgid "Has Unreconciled Entries" -msgstr "" +msgstr "Тулгагдаагүй гүйлгээтэй" #. module: account #: model:ir.model.fields,field_description:account.field_account_setup_bank_manual_config__qr_code_valid msgid "Has all required arguments" -msgstr "" +msgstr "QR код шаардлагатай" #. module: account #: model:ir.model.fields,field_description:account.field_account_invoice_send__has_error #: model:ir.model.fields,help:account.field_account_invoice_send__has_error msgid "Has error" -msgstr "" +msgstr "Алдаатай" #. module: account #: model:ir.model.fields,field_description:account.field_account_abstract_payment__hide_payment_method #: model:ir.model.fields,field_description:account.field_account_payment__hide_payment_method #: model:ir.model.fields,field_description:account.field_account_register_payments__hide_payment_method msgid "Hide Payment Method" -msgstr "" +msgstr "Төлбөрийн аргыг нуух" #. module: account #: model:ir.model.fields,field_description:account.field_account_tax__hide_tax_exigibility msgid "Hide Use Cash Basis Option" -msgstr "" +msgstr "Мөнгөн суурьт сонголтын нуух" #. module: account #: model_terms:ir.ui.view,arch_db:account.portal_invoice_chatter @@ -4962,6 +4971,7 @@ msgstr "Түүх" #: model_terms:ir.ui.view,arch_db:account.res_config_settings_view_form msgid "How total tax amount is computed in orders and invoices" msgstr "" +"Захиалга болон нэхэмжлэл дээрх татварын нийлбэр дүн хэрхэн тооцоглогдох" #. module: account #: model:ir.model.fields,field_description:account.field_account_abstract_payment__id @@ -5059,7 +5069,7 @@ msgstr "Хэрэв сонгосон бол, шинэ зурвасуудад ан #: model:ir.model.fields,help:account.field_account_invoice__message_has_error #: model:ir.model.fields,help:account.field_account_payment__message_has_error msgid "If checked, some messages have a delivery error." -msgstr "" +msgstr "Үүнийг сонговол алдаа үүсэх үед зурвасууд ирнэ." #. module: account #: model:ir.model.fields,help:account.field_account_account_template__nocreate @@ -5081,6 +5091,9 @@ msgid "" " type and recipient bank account in the generated payments. If disabled,\n" " a distinct payment will be generated for each invoice." msgstr "" +"Үүнийг сонговол, нэхэмжлэлүүдээс үүсэх төлбөрийн бүртгэлүүд харилцагч, нэхэмжлэлийн данс,\n" +" нэхэмжлэлийн төрөл болон хүлээн авагчийн банкны данс зэрэг мэдээллээр ижилсэж бүлэглэгдэнэ.\n" +" Хэрэв сонгохгүй орхивол, нэхэмжлэл бүрээс харгалзан төлбөрийн бүртгэл үүснэ." #. module: account #: model:ir.model.fields,help:account.field_account_tax__include_base_amount @@ -5098,6 +5111,8 @@ msgid "" "If set, the accounting entries created during the bank statement reconciliation process will be created at this date.\n" "This is useful if the accounting period in which the entries should normally be booked is already closed." msgstr "" +"Үүнийг сонговол, банкны хуулга тулгалтаас үүсч буй ажил гүйлгээнүүд энэ огноогоор бүртгэгдэх болно.\n" +" Хэрэв хаагдсан тайлант хугацааны гүйлгээнд тулгалт хийж байгаа бол энэ огноог зөв сонгох хэрэгтэй." #. module: account #: model:ir.model.fields,help:account.field_account_tax__analytic @@ -5115,6 +5130,8 @@ msgid "" "If the active field is set to False, it will allow you to hide the payment " "terms without removing it." msgstr "" +"Хэрэв Идэвхитэй талбарыг Үгүй утгатай болговол энэ нь танд төлбөрийн " +"нөхцөлийг устгалгүйгээр нуух боломжыг олгоно." #. module: account #: model:ir.model.fields,help:account.field_account_journal__group_invoice_lines @@ -5131,6 +5148,8 @@ msgid "" "If this checkbox is ticked, this entry will be automatically reversed at the" " reversal date you defined." msgstr "" +"Үүнийг сонгосон бол, энэ ажил гүйлгээнээс таны тохируулсан өдөр автоматаар " +"эсрэг бичилт үүсэх болно." #. module: account #: model_terms:ir.ui.view,arch_db:account.res_config_settings_view_form @@ -5421,7 +5440,7 @@ msgstr "Нөөцийн үнэлгээний оролтын данс" #. module: account #: model_terms:ir.ui.view,arch_db:account.res_config_settings_view_form msgid "Install More Packages" -msgstr "" +msgstr "Нэмэлт сангууд суулгах" #. module: account #: model:ir.model.fields,field_description:account.field_res_company__transfer_account_id @@ -5440,7 +5459,7 @@ msgstr "" #: model:ir.model.fields,field_description:account.field_account_account__internal_group #: model:ir.model.fields,field_description:account.field_account_account_type__internal_group msgid "Internal Group" -msgstr "" +msgstr "Дотоод бүлэг" #. module: account #: model:ir.model.fields,field_description:account.field_account_move__narration @@ -5529,7 +5548,7 @@ msgstr "Нэхэмжлэл #" #: code:addons/account/models/account_invoice.py:497 #, python-format msgid "Invoice - %s" -msgstr "" +msgstr "Нэхэмжлэл - %s" #. module: account #: model:mail.message.subtype,description:account.mt_invoice_created @@ -5550,12 +5569,12 @@ msgstr "Нэхэмжлэх огноо" #. module: account #: model:ir.model.fields,field_description:account.field_account_invoice__invoice_icon msgid "Invoice Icon" -msgstr "" +msgstr "Нэхэмжлэлийн дүрс" #. module: account #: model_terms:ir.ui.view,arch_db:account.onboarding_invoice_layout_step msgid "Invoice Layout" -msgstr "" +msgstr "Нэхэмжлэлийн загвар" #. module: account #: model:ir.model,name:account.model_account_invoice_line @@ -5577,12 +5596,12 @@ msgstr "Компанийн хэмжээнд нэхэмжлэлийн дугаа #. module: account #: model_terms:ir.ui.view,arch_db:account.invoice_form msgid "Invoice Number:" -msgstr "" +msgstr "Нэхэмжлэлийн дугаар:" #. module: account #: model:ir.model.fields,field_description:account.field_res_config_settings__module_account_payment msgid "Invoice Online Payment" -msgstr "" +msgstr "Нэхэмжлэл онлайнаар төлөх" #. module: account #: model:ir.model.fields,field_description:account.field_account_invoice_line__invoice_id @@ -5636,7 +5655,7 @@ msgstr "Нэхэмжлэл төлөгдсөн" #. module: account #: model_terms:ir.ui.view,arch_db:account.account_invoice_send_wizard_form msgid "Invoice send & Print" -msgstr "" +msgstr "Нэхэмжлэлийг илгээсэн & Хэвлэсэн" #. module: account #: model:mail.message.subtype,description:account.mt_invoice_validated @@ -5708,17 +5727,17 @@ msgstr "Танд өртэй нэхэмжлэлүүд" #. module: account #: model_terms:ir.ui.view,arch_db:account.account_journal_dashboard_kanban_view msgid "Invoices to Validate" -msgstr "" +msgstr "Батлах нэхэмжлэлүүд" #. module: account #: model:ir.model.fields,help:account.field_account_payment__reconciled_invoice_ids msgid "Invoices whose journal items have been reconciled with this payment's." -msgstr "" +msgstr "Энэ төлбөрөөр тулгагдсан нэхэмжлэлийн журналын бичилтүүд." #. module: account #: model:ir.actions.report,name:account.account_invoices_without_payment msgid "Invoices without Payment" -msgstr "" +msgstr "Төлөлтгүй нэхэмжлэл" #. module: account #: model:ir.ui.menu,name:account.menu_finance @@ -5732,7 +5751,7 @@ msgstr "Нэхэмжлэх" #: selection:account.reconcile.model,match_amount:0 #: selection:account.reconcile.model.template,match_amount:0 msgid "Is Between" -msgstr "" +msgstr "Хооронд" #. module: account #: model:ir.model.fields,field_description:account.field_account_bank_statement__message_is_follower @@ -5745,28 +5764,28 @@ msgstr "Дагагч эсэх" #: selection:account.reconcile.model,match_amount:0 #: selection:account.reconcile.model.template,match_amount:0 msgid "Is Greater Than" -msgstr "" +msgstr "Их эсэх" #. module: account #: selection:account.reconcile.model,match_amount:0 #: selection:account.reconcile.model.template,match_amount:0 msgid "Is Lower Than" -msgstr "" +msgstr "Бага эсэх" #. module: account #: model:ir.model.fields,field_description:account.field_account_invoice_send__printed msgid "Is Printed" -msgstr "" +msgstr "Хэвлэгдсэн эсэх" #. module: account #: model:ir.model.fields,field_description:account.field_account_reconcile_model__is_second_tax_price_included msgid "Is Second Tax Included in Price" -msgstr "" +msgstr "Үнийн дүнд хоёрдогч татвар шингэсэн эсэх" #. module: account #: model:ir.model.fields,field_description:account.field_account_reconcile_model__is_tax_price_included msgid "Is Tax Included in Price" -msgstr "" +msgstr "Татвар үнийн дүнд шингэсэн эсэх" #. module: account #: model:ir.model.fields,help:account.field_account_invoice_line__is_rounding_line @@ -5883,7 +5902,7 @@ msgstr "Журналийн бичилт" #. module: account #: model_terms:ir.ui.view,arch_db:account.view_account_move_filter msgid "Journal Entries by Date" -msgstr "" +msgstr "Ажил гүйлгээ огноогоор" #. module: account #: model:ir.model.fields,field_description:account.field_account_invoice__move_id @@ -5953,7 +5972,7 @@ msgstr "Журналын мөрүүд" #. module: account #: model:ir.actions.act_window,name:account.action_move_line_select_tax_audit msgid "Journal Items for Tax Audit" -msgstr "" +msgstr "Ажил гүйлгээ татвар аудитаар" #. module: account #. openerp-web @@ -5988,7 +6007,7 @@ msgstr "" #. module: account #: model:ir.model.fields,field_description:account.field_account_bank_statement_line__journal_currency_id msgid "Journal's Currency" -msgstr "" +msgstr "Журналын валют" #. module: account #: model:ir.actions.act_window,name:account.action_account_journal_form @@ -6026,7 +6045,7 @@ msgstr "6-р сар" #: selection:res.company,account_setup_coa_state:0 #: selection:res.company,account_setup_fy_data_state:0 msgid "Just done" -msgstr "" +msgstr "Дөнгөж дууссан" #. module: account #: model:ir.model.fields,field_description:account.field_tax_adjustments_wizard__reason @@ -6036,12 +6055,12 @@ msgstr "Баталгаажуулалт" #. module: account #: model:ir.model.fields,field_description:account.field_account_journal__kanban_dashboard msgid "Kanban Dashboard" -msgstr "" +msgstr "Канбан самбар" #. module: account #: model:ir.model.fields,field_description:account.field_account_journal__kanban_dashboard_graph msgid "Kanban Dashboard Graph" -msgstr "" +msgstr "Канбан самбар граффик" #. module: account #: model_terms:ir.ui.view,arch_db:account.view_account_journal_form @@ -6336,7 +6355,7 @@ msgstr "Хамгийн сүүлийн Нэхэмжлэл, Төлбөрийг т #. module: account #: model:ir.model.fields,field_description:account.field_account_invoice_send__layout msgid "Layout" -msgstr "" +msgstr "Хэв загвар" #. module: account #: model_terms:ir.ui.view,arch_db:account.view_account_position_form @@ -6400,7 +6419,7 @@ msgstr "Хөрвөх чадвар" #: code:addons/account/models/chart_template.py:154 #, python-format msgid "Liquidity Transfer" -msgstr "" +msgstr "Мөнгөн хөрөнгө шилжүүлгэ" #. module: account #: model:ir.model.fields,help:account.field_account_chart_template__tax_template_ids @@ -6412,7 +6431,7 @@ msgstr "Шинээр үүсгэх татваруудын жагсаалт" #: code:addons/account/static/src/xml/account_reconciliation.xml:33 #, python-format msgid "Load more" -msgstr "" +msgstr "Илүү унших" #. module: account #. openerp-web @@ -6439,7 +6458,7 @@ msgstr "Дотоод тэмдэглэл хөтлөх" #. module: account #: model_terms:ir.ui.view,arch_db:account.onboarding_invoice_layout_step msgid "Looks great!" -msgstr "" +msgstr "Дажгүй харагдаж байна!" #. module: account #: code:addons/account/models/account_bank_statement.py:175 @@ -6479,7 +6498,7 @@ msgstr "Мэйлийн жагсаалт" #: model:ir.model.fields,field_description:account.field_account_invoice__message_main_attachment_id #: model:ir.model.fields,field_description:account.field_account_payment__message_main_attachment_id msgid "Main Attachment" -msgstr "" +msgstr "Үндсэн хавсралт" #. module: account #: model:ir.model.fields,help:account.field_res_config_settings__currency_id @@ -6489,7 +6508,7 @@ msgstr "Компанийн үндсэн валют." #. module: account #: model_terms:ir.ui.view,arch_db:account.res_config_settings_view_form msgid "Main currency of your company" -msgstr "" +msgstr "Компаны үндсэн валют" #. module: account #: model:ir.ui.menu,name:account.account_management_menu @@ -6556,7 +6575,7 @@ msgstr "3-р сар" #. module: account #: model_terms:ir.ui.view,arch_db:account.res_config_settings_view_form msgid "Margin Analysis" -msgstr "" +msgstr "Ашгийн шинжилгээ" #. module: account #: selection:account.abstract.payment,payment_difference_handling:0 @@ -6578,7 +6597,7 @@ msgstr "Масс мэйлийн шинжилгээ" #. module: account #: model:ir.model.fields,field_description:account.field_account_invoice_send__mass_mailing_name msgid "Mass Mailing Name" -msgstr "" +msgstr "Масс мэйлийн нэр" #. module: account #: selection:account.reconcile.model,match_label:0 @@ -6593,17 +6612,17 @@ msgstr "" #: code:addons/account/models/chart_template.py:983 #, python-format msgid "Match existing invoices/bills." -msgstr "" +msgstr "Байгаа нэхэмжлэлтэй харгалзуулах" #. module: account #: model:ir.model.fields,field_description:account.field_account_move_line__matched_credit_ids msgid "Matched Credit" -msgstr "" +msgstr "Харгалзсан кредит" #. module: account #: model:ir.model.fields,field_description:account.field_account_move_line__matched_debit_ids msgid "Matched Debit" -msgstr "" +msgstr "Харгалзсан дебет" #. module: account #: model:ir.model.fields,field_description:account.field_account_full_reconcile__reconciled_line_ids @@ -6625,7 +6644,7 @@ msgstr "Тохирох дугаар" #. module: account #: model:ir.model.fields,field_description:account.field_account_partial_reconcile__max_date msgid "Max Date of Matched Lines" -msgstr "" +msgstr "Харгалзсан мөрүүдийн хамгийн өндөр огноо" #. module: account #: selection:res.company,fiscalyear_last_month:0 @@ -6642,7 +6661,7 @@ msgstr "Санамж" #. module: account #: model_terms:ir.ui.view,arch_db:account.view_account_payment_from_invoices msgid "Memo will be computed from invoices" -msgstr "" +msgstr "Санамж нэхэмжлэлээс тооцоологдоно" #. module: account #. openerp-web @@ -6858,7 +6877,7 @@ msgstr "Шинэ" #. module: account #: model:ir.model.fields,field_description:account.field_account_setup_bank_manual_config__new_journal_name msgid "New Journal Name" -msgstr "" +msgstr "Шинэ журналын нэр" #. module: account #: model_terms:ir.ui.view,arch_db:account.account_journal_dashboard_kanban_view @@ -6875,7 +6894,7 @@ msgstr "Төлбөрийн шинэ таамагласан огноо: " #: model:ir.model.fields,field_description:account.field_account_invoice__activity_date_deadline #: model:ir.model.fields,field_description:account.field_account_payment__activity_date_deadline msgid "Next Activity Deadline" -msgstr "" +msgstr "Дараагийн ажилбарын эцсийн огноо" #. module: account #: model:ir.model.fields,field_description:account.field_account_invoice__activity_summary @@ -6887,7 +6906,7 @@ msgstr "Дараагийн Ажилбарын Хураангуй" #: model:ir.model.fields,field_description:account.field_account_invoice__activity_type_id #: model:ir.model.fields,field_description:account.field_account_payment__activity_type_id msgid "Next Activity Type" -msgstr "" +msgstr "Дараагийн ажилбарын төрөл" #. module: account #: model:ir.model.fields,field_description:account.field_account_invoice__sequence_number_next @@ -6898,7 +6917,7 @@ msgstr "Дараагийн дугаар" #. module: account #: model:ir.model.fields,field_description:account.field_account_invoice__sequence_number_next_prefix msgid "Next Number Prefix" -msgstr "" +msgstr "Дараагийн дугаарын Угтвар" #. module: account #: model:ir.model.fields,field_description:account.field_account_move_line__blocked @@ -6916,7 +6935,7 @@ msgstr "Мессеж байхгүй" #: code:addons/account/static/src/js/reconciliation/reconciliation_renderer.js:143 #, python-format msgid "No Title" -msgstr "" +msgstr "Гарчиг байхгүй" #. module: account #: code:addons/account/models/account_invoice.py:912 @@ -6970,7 +6989,7 @@ msgstr "Энгийн Зээлдэгч" #: selection:account.reconcile.model,match_label:0 #: selection:account.reconcile.model.template,match_label:0 msgid "Not Contains" -msgstr "" +msgstr "Агуулаагүй" #. module: account #: selection:res.company,account_dashboard_onboarding_state:0 @@ -6982,7 +7001,7 @@ msgstr "" #: selection:res.company,account_setup_coa_state:0 #: selection:res.company,account_setup_fy_data_state:0 msgid "Not done" -msgstr "" +msgstr "Дуусаагүй" #. module: account #. openerp-web @@ -7021,7 +7040,7 @@ msgstr "Тэмдэглэл" #: code:addons/account/static/src/xml/account_reconciliation.xml:37 #, python-format msgid "Nothing to do!" -msgstr "" +msgstr "Хийх зүйл алга!" #. module: account #: model:ir.model.fields,field_description:account.field_account_invoice_send__notification_ids @@ -7077,7 +7096,7 @@ msgstr "Хоногийн Тоо" #: model:ir.model.fields,field_description:account.field_account_invoice__message_has_error_counter #: model:ir.model.fields,field_description:account.field_account_payment__message_has_error_counter msgid "Number of error" -msgstr "" +msgstr "Алдааны тоо" #. module: account #: model:ir.model.fields,help:account.field_account_bank_statement__message_needaction_counter @@ -7091,7 +7110,7 @@ msgstr "Үйлдэл шаардлагатай зурвасын тоо" #: model:ir.model.fields,help:account.field_account_invoice__message_has_error_counter #: model:ir.model.fields,help:account.field_account_payment__message_has_error_counter msgid "Number of messages with delivery error" -msgstr "" +msgstr "Алдааны мэдэгдэл бүхий зурвасын тоо" #. module: account #: model:ir.model.fields,help:account.field_account_bank_statement__message_unread_counter @@ -7131,7 +7150,7 @@ msgstr "" #. module: account #: model:ir.model.fields,field_description:account.field_account_move_line__tax_line_grouping_key msgid "Old Taxes" -msgstr "" +msgstr "Хуучин татвар" #. module: account #: model_terms:ir.ui.view,arch_db:account.view_payment_term_line_form @@ -7191,7 +7210,7 @@ msgstr "Нээх" #: code:addons/account/models/account_reconcile_model.py:276 #, python-format msgid "Open Balance" -msgstr "" +msgstr "Нээлтийн баланс" #. module: account #. openerp-web @@ -7204,7 +7223,7 @@ msgstr "Нээлттэй баланс" #. module: account #: model:ir.model,name:account.model_account_financial_year_op msgid "Opening Balance of Financial Year" -msgstr "" +msgstr "Санхүүгийн жилийн нээлтийн баланс" #. module: account #: model:ir.model.fields,field_description:account.field_account_financial_year_op__opening_date @@ -7215,19 +7234,19 @@ msgstr "Нээх өдөр" #. module: account #: model:ir.model.fields,field_description:account.field_res_company__account_opening_journal_id msgid "Opening Journal" -msgstr "" +msgstr "Нээлтийн журналт" #. module: account #: code:addons/account/models/company.py:365 #: model:ir.model.fields,field_description:account.field_res_company__account_opening_move_id #, python-format msgid "Opening Journal Entry" -msgstr "" +msgstr "Нээлтийн журналын бичилт" #. module: account #: model:ir.model.fields,field_description:account.field_account_financial_year_op__opening_move_posted msgid "Opening Move Posted" -msgstr "" +msgstr "Нээлтийн батлагдсан гүйлгээ" #. module: account #: model:ir.model.fields,help:account.field_account_cashbox_line__number @@ -7238,27 +7257,27 @@ msgstr "Нээлтийн нэгжийн тоонууд" #: code:addons/account/models/account.py:169 #, python-format msgid "Opening balance" -msgstr "" +msgstr "Нээлтийн баланс" #. module: account #: model:ir.model.fields,field_description:account.field_account_account__opening_credit msgid "Opening credit" -msgstr "" +msgstr "Нээлтийн кредит" #. module: account #: model:ir.model.fields,help:account.field_account_account__opening_credit msgid "Opening credit value for this account." -msgstr "" +msgstr "Энэ дансны нээлтийн кредит утга." #. module: account #: model:ir.model.fields,field_description:account.field_account_account__opening_debit msgid "Opening debit" -msgstr "" +msgstr "Нээлтийн дебет" #. module: account #: model:ir.model.fields,help:account.field_account_account__opening_debit msgid "Opening debit value for this account." -msgstr "" +msgstr "Энэ дансны нээлтийн дебет утга." #. module: account #: model_terms:ir.ui.view,arch_db:account.view_account_reconcile_model_form @@ -7399,7 +7418,7 @@ msgstr "Хугацаа хэтэрсэн нэхэмжлэлүүд, төлөх о #. module: account #: model:ir.ui.menu,name:account.menu_board_journal_1 msgid "Overview" -msgstr "" +msgstr "Тойм" #. module: account #: model_terms:ir.ui.view,arch_db:account.res_config_settings_view_form @@ -11143,7 +11162,7 @@ msgstr "" #. module: account #: model_terms:ir.ui.view,arch_db:account.res_config_settings_view_form msgid "Use follow-up levels and schedule actions" -msgstr "" +msgstr "Мөшгөлтийн төвшин болон төлөвлөгөөт арга хэмжээ ашиглах" #. module: account #: model:ir.model.fields,field_description:account.field_res_config_settings__group_products_in_bills @@ -11302,7 +11321,7 @@ msgstr "Нийлүүлэгчийн тооцоо" #: code:addons/account/models/account_invoice.py:501 #, python-format msgid "Vendor Bill - %s" -msgstr "" +msgstr "Нийлүүлэгчийн нэхэмжлэл - %s" #. module: account #: code:addons/account/models/chart_template.py:419 @@ -11318,24 +11337,24 @@ msgstr "Нийлүүлэгчийн Нэхэмжлэлүүд" #: model_terms:ir.ui.view,arch_db:account.report_invoice_document #, python-format msgid "Vendor Credit Note" -msgstr "" +msgstr "Нийлүүлэгчийн буцаалт" #. module: account #: code:addons/account/models/account_invoice.py:503 #, python-format msgid "Vendor Credit Note - %s" -msgstr "" +msgstr "Нийлүүлэгчийн буцаалт - %s" #. module: account #: code:addons/account/models/account_invoice.py:1359 #, python-format msgid "Vendor Credit note" -msgstr "" +msgstr "Нийлүүлэгчийн буцаалт" #. module: account #: model:ir.model.fields,field_description:account.field_account_invoice__vendor_display_name msgid "Vendor Display Name" -msgstr "" +msgstr "Нийлүүлэгчийн дэлгэцийн нэр" #. module: account #: code:addons/account/models/account_payment.py:774 @@ -11354,7 +11373,7 @@ msgstr "Нийлүүлэгчийн Төлбөрийн Нөхцлүүд" #: model_terms:ir.ui.view,arch_db:account.account_journal_dashboard_kanban_view #: model_terms:ir.ui.view,arch_db:account.view_account_payment_search msgid "Vendor Payments" -msgstr "" +msgstr "Нийлүүлэгчийн төлбөр" #. module: account #: model_terms:ir.ui.view,arch_db:account.invoice_supplier_form @@ -11412,7 +11431,7 @@ msgstr "Анхааруулга" #. module: account #: model:ir.model.fields,field_description:account.field_res_config_settings__group_warning_account msgid "Warnings in Invoices" -msgstr "" +msgstr "Нэхэмжлэлийн анхааруулга" #. module: account #: code:addons/account/models/company.py:541 @@ -11496,7 +11515,7 @@ msgstr "Хасалт" #: model:ir.model.fields,field_description:account.field_account_reconcile_model__amount #: model:ir.model.fields,field_description:account.field_account_reconcile_model_template__amount msgid "Write-off Amount" -msgstr "" +msgstr "Зөрүү дүн" #. module: account #: sql_constraint:account.move.line:0 @@ -11830,7 +11849,7 @@ msgstr "" #: code:addons/account/static/src/xml/account_reconciliation.xml:55 #, python-format msgid "You reconciled" -msgstr "" +msgstr "Та тулгалт хийсэн" #. module: account #: code:addons/account/models/account_move.py:1617 @@ -11962,13 +11981,6 @@ msgstr "байна. Энэ баримтыг хуваарилж төлсөнөө msgid "have been reconciled automatically." msgstr "" -#. module: account -#. openerp-web -#: code:addons/account/static/src/xml/account_reconciliation.xml:44 -#, python-format -msgid "o_manual_statement" -msgstr "" - #. module: account #: selection:account.payment.term.line,option:0 msgid "of the current month" diff --git a/addons/account/i18n/nl.po b/addons/account/i18n/nl.po index caf7adf1672..617fc9372fb 100644 --- a/addons/account/i18n/nl.po +++ b/addons/account/i18n/nl.po @@ -10005,8 +10005,8 @@ msgid "" " as it can have been generated first, and reconciled later" msgstr "" "Technisch veld bevat de facturen voor welke reeds betalingen werden gegenereerd.\n" -"Dit komt niet noodzakelijk overeen met de facturen reconsilieerd met de betalingen,\n" -"als deze eerst werden gegenereerd en pas later gereconsilieerd." +"Dit komt niet noodzakelijk overeen met de facturen afgeletterd met de betalingen,\n" +"als deze eerst werden gegenereerd en pas later afgeletterd." #. module: account #: model:ir.model.fields,help:account.field_account_invoice_line__display_type @@ -10222,9 +10222,9 @@ msgid "" " the moment the ORM reflects that change in its one2many" msgstr "" "Technische veld gebruikt om de oude veld waarde te bewaren is gebruikt om de" -" belasting regels te berekenen (in account.move from view) op het ogenblik " -"dat de gebruiker het wijzigde en het ogenblik dat de ORM deze wijziging " -"aangeeft in zijn one2many " +" belasting regels te berekenen (in account.move formulier weergave) op het " +"ogenblik dat de gebruiker het wijzigde en het ogenblik dat de ORM deze " +"wijziging aangeeft in zijn one2many " #. module: account #: model:ir.model.fields,field_description:account.field_res_config_settings__chart_template_id @@ -12027,8 +12027,8 @@ msgid "" "You can not have an overlap between two fiscal years, please correct the " "start and/or end dates of your fiscal years." msgstr "" -"U kunt geen overlapping hebben tussen twee belastbare jaren, wijzig aub de " -"start en/of eind datum van uw belastbare jaren." +"U kunt geen overlapping hebben tussen twee fiscale jaren, wijzig aub de " +"start en/of einddatum van uw belastbare jaren." #. module: account #: code:addons/account/models/account_invoice.py:1827 @@ -12100,8 +12100,8 @@ msgid "" "You cannot create journal items with a secondary currency without filling " "both 'currency' and 'amount currency' fields." msgstr "" -"U kunt geen joernaalposten aanmaken met een tweede valuta zonder invulling " -"van beide 'valuta' en 'bedrag valuta' velden." +"U kunt geen dagboeken aanmaken met een tweede valuta zonder invulling van " +"beide 'valuta' en 'bedrag valuta' velden." #. module: account #: code:addons/account/models/company.py:187 @@ -12290,8 +12290,8 @@ msgid "" "You cannot switch an account to prevent the reconciliation if some partial " "reconciliations are still pending." msgstr "" -"You cannot switch an account to prevent the reconciliation if some partial " -"reconciliations are still pending." +"U kan niet wisselen van account om het afletteren te voorkomen als " +"gedeeltelijke afletteringen nog in aanvraag zijn." #. module: account #: code:addons/account/models/account_move.py:1102 diff --git a/addons/account/i18n/ro.po b/addons/account/i18n/ro.po index 7001c5548eb..a8b2c9271da 100644 --- a/addons/account/i18n/ro.po +++ b/addons/account/i18n/ro.po @@ -2827,6 +2827,8 @@ msgid "" "Click to validate your invoice. A reference will be assigned to this " "invoice and you will not be able to modify it anymore." msgstr "" +"Faceți clic pentru a valida factura. O referință va fi atribuită " +"acestei facturi și nu o veți mai putea modifica." #. module: account #. openerp-web @@ -11892,13 +11894,6 @@ msgstr "" msgid "have been reconciled automatically." msgstr "" -#. module: account -#. openerp-web -#: code:addons/account/static/src/xml/account_reconciliation.xml:44 -#, python-format -msgid "o_manual_statement" -msgstr "" - #. module: account #: selection:account.payment.term.line,option:0 msgid "of the current month" diff --git a/addons/account/i18n/sv.po b/addons/account/i18n/sv.po index 3b1c0416f00..a75fcdb2e45 100644 --- a/addons/account/i18n/sv.po +++ b/addons/account/i18n/sv.po @@ -2993,7 +2993,7 @@ msgstr "" #. module: account #: model:ir.model,name:account.model_res_config_settings msgid "Config Settings" -msgstr "" +msgstr "Konfigurations Inställningar" #. module: account #: model:ir.ui.menu,name:account.menu_finance_configuration diff --git a/addons/account/i18n/uk.po b/addons/account/i18n/uk.po index 08fe21338d1..485989d2cd2 100644 --- a/addons/account/i18n/uk.po +++ b/addons/account/i18n/uk.po @@ -2911,7 +2911,7 @@ msgstr "" #: code:addons/account/static/src/xml/account_reconciliation.xml:17 #, python-format msgid "Click to Rename" -msgstr "" +msgstr "Натисніть, щоб змінити назву" #. module: account #. openerp-web @@ -3077,7 +3077,7 @@ msgstr "Повний набір податків" #. module: account #: model:ir.model.fields,field_description:account.field_account_invoice_send__composer_id msgid "Composer" -msgstr "" +msgstr "Автор" #. module: account #: model:ir.model.fields,field_description:account.field_account_invoice_send__composition_mode @@ -3106,7 +3106,7 @@ msgstr "Розрахований баланс" #. module: account #: model_terms:ir.ui.view,arch_db:account.view_account_reconcile_model_form msgid "Conditions on Bank Statement Line" -msgstr "" +msgstr "Умови на рядку банківського рахунку" #. module: account #: model:ir.model,name:account.model_res_config_settings @@ -3129,12 +3129,12 @@ msgstr "Налаштування" #: code:addons/account/static/src/js/section_and_note_fields_backend.js:102 #, python-format msgid "Configure a product" -msgstr "" +msgstr "Налаштувати товар" #. module: account #: model:ir.actions.act_window,name:account.action_open_account_onboarding_invoice_layout msgid "Configure your document layout" -msgstr "" +msgstr "Налаштуйте макет документа" #. module: account #: model_terms:ir.ui.view,arch_db:account.view_account_bnk_stmt_cashbox @@ -3188,7 +3188,7 @@ msgstr "Вітаємо, ви все завершили!" #. module: account #: model_terms:ir.ui.view,arch_db:account.account_invoice_onboarding_panel msgid "Congratulations! You are all set." -msgstr "" +msgstr "Вітаємо! Ви все налаштували." #. module: account #: model:ir.model,name:account.model_res_partner @@ -3218,7 +3218,7 @@ msgstr "Містить" #: model:ir.model.fields,field_description:account.field_res_partner__contracts_count #: model:ir.model.fields,field_description:account.field_res_users__contracts_count msgid "Contracts Count" -msgstr "" +msgstr "Підрахунок контрактів" #. module: account #: model_terms:ir.ui.view,arch_db:account.view_account_journal_form @@ -5765,6 +5765,8 @@ msgid "" "Invoice_${(object.number or '').replace('/','_')}${object.state == 'draft' " "and '_draft' or ''}" msgstr "" +"Invoice_${(object.number or '').replace('/','_')}${object.state == 'draft' " +"and '_draft' or ''}" #. module: account #: model_terms:ir.ui.view,arch_db:account.view_account_invoice_report_search diff --git a/addons/account/i18n/zh_TW.po b/addons/account/i18n/zh_TW.po index 0cb0eeb472d..7a0ac8e0273 100644 --- a/addons/account/i18n/zh_TW.po +++ b/addons/account/i18n/zh_TW.po @@ -12,6 +12,7 @@ # Andy Cheng , 2019 # sejun huang , 2019 # 敬雲 林 , 2019 +# Ying Chang , 2019 # msgid "" msgstr "" @@ -19,7 +20,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: 敬雲 林 , 2019\n" +"Last-Translator: Ying Chang , 2019\n" "Language-Team: Chinese (Taiwan) (https://www.transifex.com/odoo/teams/41243/zh_TW/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -248,7 +249,7 @@ msgstr "" #. module: account #: model_terms:ir.ui.view,arch_db:account.view_account_payment_kanban msgid "" -msgstr "" +msgstr "" #. module: account #: model_terms:ir.ui.view,arch_db:account.portal_invoice_page @@ -341,6 +342,9 @@ msgid "" "specific.\" aria-label=\"Values set here are company-specific.\" " "groups=\"base.group_multi_company\" role=\"img\"/>" msgstr "" +"" #. module: account #: model_terms:ir.ui.view,arch_db:account.res_config_settings_view_form @@ -812,7 +816,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 @@ -1689,7 +1693,7 @@ msgstr "金額:" #: sql_constraint:account.fiscal.position.account:0 msgid "" "An account fiscal position could be defined only one time on same accounts." -msgstr "" +msgstr "財政狀況只能在一個科目上定義一次。" #. module: account #: model_terms:ir.actions.act_window,help:account.action_account_form @@ -4608,12 +4612,12 @@ msgstr "財務年度" #. module: account #: model:ir.model.fields,help:account.field_account_financial_year_op__fiscalyear_last_day msgid "Fiscal year last day." -msgstr "" +msgstr "會計年度的最後一天" #. module: account #: model:ir.model.fields,help:account.field_account_financial_year_op__fiscalyear_last_month msgid "Fiscal year last month." -msgstr "" +msgstr "會計年度的最後一個月" #. module: account #: model:ir.model.fields,field_description:account.field_account_financial_year_op__fiscalyear_last_day @@ -5489,7 +5493,7 @@ msgstr "無效的\"郵編範圍\",請正確的配置。" #: 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 @@ -11870,13 +11874,6 @@ msgstr "對於此供應商,您可以將它們分配為標記此帳單已付款 msgid "have been reconciled automatically." msgstr "已自動調帳" -#. module: account -#. openerp-web -#: code:addons/account/static/src/xml/account_reconciliation.xml:44 -#, python-format -msgid "o_manual_statement" -msgstr "o_manual_statement" - #. module: account #: selection:account.payment.term.line,option:0 msgid "of the current month" diff --git a/addons/account/models/account.py b/addons/account/models/account.py index 0730a364a5c..e8a5b60af4c 100644 --- a/addons/account/models/account.py +++ b/addons/account/models/account.py @@ -482,7 +482,7 @@ class AccountJournal(models.Model): post_at_bank_rec = fields.Boolean(string="Post At Bank Reconciliation", help="Whether or not the payments made in this journal should be generated in draft state, so that the related journal entries are only posted when performing bank reconciliation.") # alias configuration for 'purchase' type journals - alias_id = fields.Many2one('mail.alias', string='Alias') + alias_id = fields.Many2one('mail.alias', string='Alias', copy=False) alias_domain = fields.Char('Alias domain', compute='_compute_alias_domain', default=lambda self: self.env["ir.config_parameter"].sudo().get_param("mail.catchall.domain")) alias_name = fields.Char('Alias Name for Vendor Bills', related='alias_id.alias_name', help="It creates draft vendor bill by sending an email.", readonly=False) diff --git a/addons/account/models/account_bank_statement.py b/addons/account/models/account_bank_statement.py index 778c62d1d61..2695db40ee6 100644 --- a/addons/account/models/account_bank_statement.py +++ b/addons/account/models/account_bank_statement.py @@ -604,8 +604,11 @@ class AccountBankStatementLine(models.Model): # Fully reconciled moves are just linked to the bank statement total = self.amount + currency = self.currency_id or statement_currency for aml_rec in payment_aml_rec: - total -= aml_rec.debit - aml_rec.credit + balance = aml_rec.amount_currency if aml_rec.currency_id else aml_rec.balance + aml_currency = aml_rec.currency_id or aml_rec.company_currency_id + total -= aml_currency._convert(balance, currency, aml_rec.company_id, aml_rec.date) aml_rec.with_context(check_move_validity=False).write({'statement_line_id': self.id}) counterpart_moves = (counterpart_moves | aml_rec.move_id) if aml_rec.journal_id.post_at_bank_rec and aml_rec.payment_id and aml_rec.move_id.state == 'draft': diff --git a/addons/account/models/account_invoice.py b/addons/account/models/account_invoice.py index 2a9218ad65a..fd08620f992 100644 --- a/addons/account/models/account_invoice.py +++ b/addons/account/models/account_invoice.py @@ -123,7 +123,7 @@ class AccountInvoice(models.Model): self.residual_signed = abs(residual) * sign self.residual = abs(residual) digits_rounding_precision = self.currency_id.rounding - if float_is_zero(self.residual, precision_rounding=digits_rounding_precision): + if self.move_id and float_is_zero(self.residual, precision_rounding=digits_rounding_precision): self.reconciled = True else: self.reconciled = False diff --git a/addons/account/models/account_move.py b/addons/account/models/account_move.py index ec3321184b3..605c09784a3 100644 --- a/addons/account/models/account_move.py +++ b/addons/account/models/account_move.py @@ -410,18 +410,20 @@ class AccountMove(models.Model): @api.multi def _reverse_move(self, date=None, journal_id=None, auto=False): self.ensure_one() - reversed_move = self.copy(default={ - 'date': date, - 'journal_id': journal_id.id if journal_id else self.journal_id.id, - 'ref': (_('Automatic reversal of: %s') if auto else _('Reversal of: %s')) % (self.name), - 'auto_reverse': False}) - for acm_line in reversed_move.line_ids.with_context(check_move_validity=False): - acm_line.write({ - 'debit': acm_line.credit, - 'credit': acm_line.debit, - 'amount_currency': -acm_line.amount_currency - }) - self.reverse_entry_id = reversed_move + with self.env.norecompute(): + reversed_move = self.copy(default={ + 'date': date, + 'journal_id': journal_id.id if journal_id else self.journal_id.id, + 'ref': (_('Automatic reversal of: %s') if auto else _('Reversal of: %s')) % (self.name), + 'auto_reverse': False}) + for acm_line in reversed_move.line_ids.with_context(check_move_validity=False): + acm_line.write({ + 'debit': acm_line.credit, + 'credit': acm_line.debit, + 'amount_currency': -acm_line.amount_currency + }) + self.reverse_entry_id = reversed_move + self.recompute() return reversed_move @api.multi @@ -550,7 +552,7 @@ class AccountMoveLine(models.Model): #computing the `reconciled` field. reconciled = False digits_rounding_precision = line.company_id.currency_id.rounding - if float_is_zero(amount, precision_rounding=digits_rounding_precision): + if (line.matched_debit_ids or line.matched_credit_ids) and float_is_zero(amount, precision_rounding=digits_rounding_precision): if line.currency_id and line.amount_currency: if float_is_zero(amount_residual_currency, precision_rounding=line.currency_id.rounding): reconciled = True @@ -932,7 +934,7 @@ class AccountMoveLine(models.Model): for line in self: company_ids.add(line.company_id.id) all_accounts.append(line.account_id) - if line.reconciled: + if (line.matched_debit_ids or line.matched_credit_ids) and line.reconciled: raise UserError(_('You are trying to reconcile some entries that are already reconciled.')) if len(company_ids) > 1: raise UserError(_('To reconcile the entries company should be the same for all entries.')) @@ -1081,6 +1083,56 @@ class AccountMoveLine(models.Model): ) return rec_move_ids.unlink() + def _apply_taxes(self, vals, amount): + tax_lines_vals = [] + # Get ids from triplets : https://www.odoo.com/documentation/10.0/reference/orm.html#odoo.models.Model.write + tax_ids = [tax['id'] for tax in self.resolve_2many_commands('tax_ids', vals['tax_ids']) if tax.get('id')] + # Since create() receives ids instead of recordset, let's just use the old-api bridge + taxes = self.env['account.tax'].browse(tax_ids) + currency = self.env['res.currency'].browse(vals.get('currency_id')) + partner = self.env['res.partner'].browse(vals.get('partner_id')) + ctx = dict(self._context) + ctx['round'] = ctx.get('round', True) + res = taxes.with_context(ctx).compute_all(amount, + currency, 1, vals.get('product_id'), partner) + # Adjust line amount if any tax is price_include + if abs(res['total_excluded']) < abs(amount): + if vals['debit'] != 0.0: vals['debit'] = res['total_excluded'] + if vals['credit'] != 0.0: vals['credit'] = -res['total_excluded'] + if vals.get('amount_currency'): + vals['amount_currency'] = self.env['res.currency'].browse(vals['currency_id']).round(vals['amount_currency'] * (res['total_excluded']/amount)) + # Create tax lines + for tax_vals in res['taxes']: + if tax_vals['amount']: + tax = self.env['account.tax'].browse([tax_vals['id']]) + account_id = (amount > 0 and tax_vals['account_id'] or tax_vals['refund_account_id']) + if not account_id: account_id = vals['account_id'] + temp = { + 'account_id': account_id, + 'name': vals['name'] + ' ' + tax_vals['name'], + 'tax_line_id': tax_vals['id'], + 'move_id': vals['move_id'], + 'partner_id': vals.get('partner_id'), + 'statement_id': vals.get('statement_id'), + 'debit': tax_vals['amount'] > 0 and tax_vals['amount'] or 0.0, + 'credit': tax_vals['amount'] < 0 and -tax_vals['amount'] or 0.0, + 'analytic_account_id': vals.get('analytic_account_id') if tax.analytic else False, + } + bank = self.env["account.bank.statement.line"].browse(vals.get('statement_line_id')).statement_id + if bank.currency_id != bank.company_id.currency_id: + ctx = {} + if 'date' in vals: + ctx['date'] = vals['date'] + elif 'date_maturity' in vals: + ctx['date'] = vals['date_maturity'] + temp['currency_id'] = bank.currency_id.id + temp['amount_currency'] = bank.company_id.currency_id.with_context(ctx).compute(tax_vals['amount'], bank.currency_id, round=True) + if vals.get('tax_exigible'): + temp['tax_exigible'] = True + temp['account_id'] = tax.cash_basis_account.id or account_id + tax_lines_vals.append(temp) + return tax_lines_vals + #################################################### # CRUD methods #################################################### @@ -1522,90 +1574,91 @@ class AccountPartialReconcile(models.Model): self.ensure_one() move_date = self.debit_move_id.date newly_created_move = self.env['account.move'] - for move in (self.debit_move_id.move_id, self.credit_move_id.move_id): - #move_date is the max of the 2 reconciled items - if move_date < move.date: - move_date = move.date - percentage_before = percentage_before_rec[move.id] - percentage_after = move.line_ids[0]._get_matched_percentage()[move.id] - # update the percentage before as the move can be part of - # multiple partial reconciliations - percentage_before_rec[move.id] = percentage_after + with self.env.norecompute(): + for move in (self.debit_move_id.move_id, self.credit_move_id.move_id): + #move_date is the max of the 2 reconciled items + if move_date < move.date: + move_date = move.date + percentage_before = percentage_before_rec[move.id] + percentage_after = move.line_ids[0]._get_matched_percentage()[move.id] + # update the percentage before as the move can be part of + # multiple partial reconciliations + percentage_before_rec[move.id] = percentage_after - for line in move.line_ids: - if not line.tax_exigible: - #amount is the current cash_basis amount minus the one before the reconciliation - amount = line.balance * percentage_after - line.balance * percentage_before - rounded_amt = self._get_amount_tax_cash_basis(amount, line) - if float_is_zero(rounded_amt, precision_rounding=line.company_id.currency_id.rounding): - continue - if line.tax_line_id and line.tax_line_id.tax_exigibility == 'on_payment': - if not newly_created_move: - newly_created_move = self._create_tax_basis_move() - #create cash basis entry for the tax line - to_clear_aml = self.env['account.move.line'].with_context(check_move_validity=False).create({ - 'name': line.move_id.name, - 'debit': abs(rounded_amt) if rounded_amt < 0 else 0.0, - 'credit': rounded_amt if rounded_amt > 0 else 0.0, - 'account_id': line.account_id.id, - 'analytic_account_id': line.analytic_account_id.id, - 'analytic_tag_ids': line.analytic_tag_ids.ids, - 'tax_exigible': True, - 'amount_currency': line.amount_currency and line.currency_id.round(-line.amount_currency * amount / line.balance) or 0.0, - 'currency_id': line.currency_id.id, - 'move_id': newly_created_move.id, - 'partner_id': line.partner_id.id, - }) - # Group by cash basis account and tax - self.env['account.move.line'].with_context(check_move_validity=False).create({ - 'name': line.name, - 'debit': rounded_amt if rounded_amt > 0 else 0.0, - 'credit': abs(rounded_amt) if rounded_amt < 0 else 0.0, - 'account_id': line.tax_line_id.cash_basis_account_id.id, - 'analytic_account_id': line.analytic_account_id.id, - 'analytic_tag_ids': line.analytic_tag_ids.ids, - 'tax_line_id': line.tax_line_id.id, - 'tax_exigible': True, - 'amount_currency': line.amount_currency and line.currency_id.round(line.amount_currency * amount / line.balance) or 0.0, - 'currency_id': line.currency_id.id, - 'move_id': newly_created_move.id, - 'partner_id': line.partner_id.id, - }) - if line.account_id.reconcile: - #setting the account to allow reconciliation will help to fix rounding errors - to_clear_aml |= line - to_clear_aml.reconcile() - - if any([tax.tax_exigibility == 'on_payment' for tax in line.tax_ids]): - if not newly_created_move: - newly_created_move = self._create_tax_basis_move() - #create cash basis entry for the base - for tax in line.tax_ids.filtered(lambda t: t.tax_exigibility == 'on_payment'): - account_id = self._get_tax_cash_basis_base_account(line, tax) - self.env['account.move.line'].with_context(check_move_validity=False).create({ - 'name': line.name, - 'debit': rounded_amt > 0 and rounded_amt or 0.0, - 'credit': rounded_amt < 0 and abs(rounded_amt) or 0.0, - 'account_id': account_id.id, + for line in move.line_ids: + if not line.tax_exigible: + #amount is the current cash_basis amount minus the one before the reconciliation + amount = line.balance * percentage_after - line.balance * percentage_before + rounded_amt = self._get_amount_tax_cash_basis(amount, line) + if float_is_zero(rounded_amt, precision_rounding=line.company_id.currency_id.rounding): + continue + if line.tax_line_id and line.tax_line_id.tax_exigibility == 'on_payment': + if not newly_created_move: + newly_created_move = self._create_tax_basis_move() + #create cash basis entry for the tax line + to_clear_aml = self.env['account.move.line'].with_context(check_move_validity=False).create({ + 'name': line.move_id.name, + 'debit': abs(rounded_amt) if rounded_amt < 0 else 0.0, + 'credit': rounded_amt if rounded_amt > 0 else 0.0, + 'account_id': line.account_id.id, + 'analytic_account_id': line.analytic_account_id.id, + 'analytic_tag_ids': line.analytic_tag_ids.ids, 'tax_exigible': True, - 'tax_ids': [(6, 0, [tax.id])], - 'move_id': newly_created_move.id, + 'amount_currency': line.amount_currency and line.currency_id.round(-line.amount_currency * amount / line.balance) or 0.0, 'currency_id': line.currency_id.id, - 'amount_currency': self.amount_currency and line.currency_id.round(line.amount_currency * amount / line.balance) or 0.0, + 'move_id': newly_created_move.id, 'partner_id': line.partner_id.id, }) + # Group by cash basis account and tax self.env['account.move.line'].with_context(check_move_validity=False).create({ 'name': line.name, - 'credit': rounded_amt > 0 and rounded_amt or 0.0, - 'debit': rounded_amt < 0 and abs(rounded_amt) or 0.0, - 'account_id': account_id.id, + 'debit': rounded_amt if rounded_amt > 0 else 0.0, + 'credit': abs(rounded_amt) if rounded_amt < 0 else 0.0, + 'account_id': line.tax_line_id.cash_basis_account_id.id, + 'analytic_account_id': line.analytic_account_id.id, + 'analytic_tag_ids': line.analytic_tag_ids.ids, + 'tax_line_id': line.tax_line_id.id, 'tax_exigible': True, - 'move_id': newly_created_move.id, + 'amount_currency': line.amount_currency and line.currency_id.round(line.amount_currency * amount / line.balance) or 0.0, 'currency_id': line.currency_id.id, - 'amount_currency': self.amount_currency and line.currency_id.round(-line.amount_currency * amount / line.balance) or 0.0, + 'move_id': newly_created_move.id, 'partner_id': line.partner_id.id, }) + if line.account_id.reconcile: + #setting the account to allow reconciliation will help to fix rounding errors + to_clear_aml |= line + to_clear_aml.reconcile() + if any([tax.tax_exigibility == 'on_payment' for tax in line.tax_ids]): + if not newly_created_move: + newly_created_move = self._create_tax_basis_move() + #create cash basis entry for the base + for tax in line.tax_ids.filtered(lambda t: t.tax_exigibility == 'on_payment'): + account_id = self._get_tax_cash_basis_base_account(line, tax) + self.env['account.move.line'].with_context(check_move_validity=False).create({ + 'name': line.name, + 'debit': rounded_amt > 0 and rounded_amt or 0.0, + 'credit': rounded_amt < 0 and abs(rounded_amt) or 0.0, + 'account_id': account_id.id, + 'tax_exigible': True, + 'tax_ids': [(6, 0, [tax.id])], + 'move_id': newly_created_move.id, + 'currency_id': line.currency_id.id, + 'amount_currency': self.amount_currency and line.currency_id.round(line.amount_currency * amount / line.balance) or 0.0, + 'partner_id': line.partner_id.id, + }) + self.env['account.move.line'].with_context(check_move_validity=False).create({ + 'name': line.name, + 'credit': rounded_amt > 0 and rounded_amt or 0.0, + 'debit': rounded_amt < 0 and abs(rounded_amt) or 0.0, + 'account_id': account_id.id, + 'tax_exigible': True, + 'move_id': newly_created_move.id, + 'currency_id': line.currency_id.id, + 'amount_currency': self.amount_currency and line.currency_id.round(-line.amount_currency * amount / line.balance) or 0.0, + 'partner_id': line.partner_id.id, + }) + self.recompute() if newly_created_move: if move_date > (self.company_id.period_lock_date or date.min) and newly_created_move.date != move_date: # The move date should be the maximum date between payment and invoice (in case diff --git a/addons/account/models/account_payment.py b/addons/account/models/account_payment.py index dd6ec32f869..7646343f7a4 100644 --- a/addons/account/models/account_payment.py +++ b/addons/account/models/account_payment.py @@ -211,6 +211,8 @@ class account_abstract_payment(models.AbstractModel): self.amount = abs(self._compute_payment_amount()) # Set by default the first liquidity journal having this currency if exists. + if self.journal_id: + return journal = self.env['account.journal'].search( [('type', 'in', ('bank', 'cash')), ('currency_id', '=', self.currency_id.id)], limit=1) if journal: diff --git a/addons/account/models/chart_template.py b/addons/account/models/chart_template.py index 2cc7dd7e791..108538368b8 100644 --- a/addons/account/models/chart_template.py +++ b/addons/account/models/chart_template.py @@ -173,7 +173,7 @@ class AccountChartTemplate(models.Model): # (won't work well for multi-company) company = self.env.user.company_id # If we don't have any chart of account on this company, install this chart of account - if not self.existing_accounting(company): + if not company.chart_template_id and not self.existing_accounting(company): self.load_for_current_company(15.0, 15.0) def load_for_current_company(self, sale_tax_rate, purchase_tax_rate): diff --git a/addons/account/security/ir.model.access.csv b/addons/account/security/ir.model.access.csv index ef09176438b..a091adbd8b8 100644 --- a/addons/account/security/ir.model.access.csv +++ b/addons/account/security/ir.model.access.csv @@ -68,7 +68,7 @@ access_account_account_type_invoice,account.account.type invoice,model_account_a access_account_account_tax,account.account.tag,model_account_account_tag,account.group_account_user,1,1,1,1 access_account_account_tax_user,account.account.tag,model_account_account_tag,account.group_account_invoice,1,0,0,0 access_account_reconcile_model,account.reconcile.model,model_account_reconcile_model,account.group_account_user,1,1,1,1 -access_account_reconcile_model_template,account.reconcile.model.template,model_account_reconcile_model_template,account.group_account_user,1,1,1,1 +access_account_reconcile_model_template,account.reconcile.model.template,model_account_reconcile_model_template,account.group_account_invoice,1,1,1,1 access_account_partial_reconcile_group_invoice,account.partial.reconcile.group.invoice,model_account_partial_reconcile,account.group_account_invoice,1,1,1,0 access_account_partial_reconcile,account.partial.reconcile,model_account_partial_reconcile,account.group_account_user,1,1,1,1 access_account_full_reconcile_group_invoice,account.full.reconcile.group.invoice,model_account_full_reconcile,account.group_account_invoice,1,1,1,0 diff --git a/addons/account/static/src/js/reconciliation/reconciliation_model.js b/addons/account/static/src/js/reconciliation/reconciliation_model.js index 91b200c2d93..1d5eee2a700 100644 --- a/addons/account/static/src/js/reconciliation/reconciliation_model.js +++ b/addons/account/static/src/js/reconciliation/reconciliation_model.js @@ -925,11 +925,11 @@ var StatementModel = BasicModel.extend({ }); var company_currency = session.get_currency(line.st_line.currency_id); var company_precision = company_currency && company_currency.digits[1] || 2; - total = utils.round_decimals(total*1000, company_precision)/1000 || 0; + total = utils.round_decimals(total, company_precision) || 0; if(isOtherCurrencyId){ var other_currency = session.get_currency(isOtherCurrencyId); var other_precision = other_currency && other_currency.digits[1] || 2; - amount_currency = utils.round_decimals(amount_currency, other_precision) + amount_currency = utils.round_decimals(amount_currency, other_precision); } line.balance = { amount: total, diff --git a/addons/account/static/src/js/reconciliation/reconciliation_renderer.js b/addons/account/static/src/js/reconciliation/reconciliation_renderer.js index 0d4a025c28c..1543615a491 100644 --- a/addons/account/static/src/js/reconciliation/reconciliation_renderer.js +++ b/addons/account/static/src/js/reconciliation/reconciliation_renderer.js @@ -262,7 +262,7 @@ var LineRenderer = Widget.extend(FieldManagerMixin, { 'click .accounting_view thead td': '_onTogglePanel', 'click .accounting_view tfoot td:not(.cell_left,.cell_right)': '_onShowPanel', 'click .cell': '_onEditAmount', - 'input input.filter': '_onFilterChange', + 'change input.filter': '_onFilterChange', 'click .match .load-more a': '_onLoadMore', 'click .match .mv_line td': '_onSelectMoveLine', 'click .accounting_view tbody .mv_line td': '_onSelectProposition', diff --git a/addons/account/static/src/xml/account_reconciliation.xml b/addons/account/static/src/xml/account_reconciliation.xml index e21f5124c9b..57822f75b09 100644 --- a/addons/account/static/src/xml/account_reconciliation.xml +++ b/addons/account/static/src/xml/account_reconciliation.xml @@ -149,7 +149,8 @@
- + +
diff --git a/addons/account/tests/account_test_no_chart.py b/addons/account/tests/account_test_no_chart.py index 05aa54df4c6..ca1c971d0a6 100644 --- a/addons/account/tests/account_test_no_chart.py +++ b/addons/account/tests/account_test_no_chart.py @@ -46,6 +46,12 @@ class TestAccountNoChartCommon(SavepointCase): 'property_account_receivable_id': cls.account_receivable.id, }) + cls.sale_journal0 = cls.env['account.journal'].create({ + 'name': 'Sale Journal', + 'type': 'sale', + 'code': 'SJT0', + }) + @classmethod def setUpAdditionalAccounts(cls): """ Set up some addionnal accounts: expenses, revenue, ... """ diff --git a/addons/account/tests/test_reconciliation.py b/addons/account/tests/test_reconciliation.py index b077d6112cf..ca35292af5a 100644 --- a/addons/account/tests/test_reconciliation.py +++ b/addons/account/tests/test_reconciliation.py @@ -530,6 +530,14 @@ class TestReconciliation(AccountingTestCase): self.diff_income_account.id: {'debit': 0.0, 'credit': 3.27, 'amount_currency': -5, 'currency_id': self.currency_usd_id}, self.account_rcv.id: {'debit': 0.0, 'credit': 52.33, 'amount_currency': -80, 'currency_id': self.currency_usd_id}, } + + payments = bank_stmt_aml.mapped('payment_id') + # creation and reconciliation of the over-amount statement + # has created an another payment + self.assertEqual(len(payments), 2) + # Check amount of second, automatically created payment + self.assertEqual((payments - payment).amount, 5) + for aml in bank_stmt_aml: line = lines[aml.account_id.id] if type(line) == list: diff --git a/addons/account/views/res_config_settings_views.xml b/addons/account/views/res_config_settings_views.xml index fb263e4be5a..6b4a5df4983 100644 --- a/addons/account/views/res_config_settings_views.xml +++ b/addons/account/views/res_config_settings_views.xml @@ -96,7 +96,6 @@
- % set company, user = ctx['company'], ctx['user'] + % set user = ctx.get('user', user) + % set company = user.company_id % set data = object.compute_kpis(company, user) % set tips = object.compute_tips(company, user) % set kpi_actions = object.compute_kpis_actions(company, user) @@ -136,7 +137,7 @@ - - diff --git a/addons/hr_skills/views/hr_views.xml b/addons/hr_skills/views/hr_views.xml index b85d4f4d0e4..e5cc42bfc4e 100644 --- a/addons/hr_skills/views/hr_views.xml +++ b/addons/hr_skills/views/hr_views.xml @@ -49,7 +49,7 @@ - + @@ -62,7 +62,7 @@
- + @@ -89,7 +89,7 @@ - + @@ -102,7 +102,7 @@
- + diff --git a/addons/im_livechat/models/im_livechat_channel.py b/addons/im_livechat/models/im_livechat_channel.py index 42a229ec7bb..df57a6c770e 100644 --- a/addons/im_livechat/models/im_livechat_channel.py +++ b/addons/im_livechat/models/im_livechat_channel.py @@ -166,6 +166,7 @@ class ImLivechatChannel(models.Model): 'public': 'private', 'email_send': False, }) + mail_channel._broadcast([operator_partner_id]) return mail_channel.sudo().channel_info()[0] @api.model diff --git a/addons/l10n_ch/i18n_extra/de.po b/addons/l10n_ch/i18n_extra/de.po index acfab8ce872..278f2bf8a0f 100644 --- a/addons/l10n_ch/i18n_extra/de.po +++ b/addons/l10n_ch/i18n_extra/de.po @@ -4,26 +4,25 @@ # msgid "" msgstr "" -"Project-Id-Version: Odoo Server 9.0c\n" +"Project-Id-Version: Odoo Server 12.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-03-14 15:52+0000\n" -"PO-Revision-Date: 2016-12-15 14:35+0100\n" -"Last-Translator: \n" +"POT-Creation-Date: 2019-02-12 05:39+0000\n" +"PO-Revision-Date: 2019-02-12 12:06+0530\n" +"Last-Translator: didier.donze@camptocamp.com\n" "Language-Team: \n" "Language: de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: \n" +"X-Generator: Poedit 2.2.1\n" #. module: l10n_ch -#: model:account.tax,description:l10n_ch.1_vat_O_exclude #: model:account.tax.template,description:l10n_ch.vat_O_exclude msgid "0% excl." msgstr "0% Exkl." #. module: l10n_ch -#: model:account.tax,description:l10n_ch.1_vat_O_import #: model:account.tax.template,description:l10n_ch.vat_O_import msgid "0% import." msgstr "0% Import." @@ -34,118 +33,105 @@ msgid "100% dédouanement TVA" msgstr "100% Verzollung MwSt." #. module: l10n_ch -#: model:account.tax,description:l10n_ch.1_vat_100_import #: model:account.tax.template,description:l10n_ch.vat_100_import msgid "100% imp." msgstr "100% Imp." #. module: l10n_ch -#: model:account.tax,description:l10n_ch.1_vat_25 #: model:account.tax.template,description:l10n_ch.vat_25 msgid "2.5%" msgstr "2.5%" #. module: l10n_ch -#: model:account.tax,description:l10n_ch.1_vat_25_incl #: model:account.tax.template,description:l10n_ch.vat_25_incl msgid "2.5% Incl." msgstr "2.5% Inkl." #. module: l10n_ch -#: model:account.tax,description:l10n_ch.1_vat_25_purchase #: model:account.tax.template,description:l10n_ch.vat_25_purchase msgid "2.5% achat" msgstr "2.5% Einkauf" #. module: l10n_ch -#: model:account.tax,description:l10n_ch.1_vat_25_purchase_incl #: model:account.tax.template,description:l10n_ch.vat_25_purchase_incl msgid "2.5% achat Incl." msgstr "2.5% Einkauf Inkl." #. module: l10n_ch -#: model:account.tax,description:l10n_ch.1_vat_25_invest #: model:account.tax.template,description:l10n_ch.vat_25_invest msgid "2.5% invest." msgstr "2.5% Invest." #. module: l10n_ch -#: model:account.tax,description:l10n_ch.1_vat_25_invest_incl #: model:account.tax.template,description:l10n_ch.vat_25_invest_incl msgid "2.5% invest. Incl." msgstr "2.5% Invest. Inkl." #. module: l10n_ch -#: model:account.tax,description:l10n_ch.1_vat_38 -#: model:account.tax.template,description:l10n_ch.vat_38 -msgid "3.8%" -msgstr "3.8%" +#: model:account.tax.template,description:l10n_ch.vat_37 +msgid "3.7%" +msgstr "3.7%" #. module: l10n_ch -#: model:account.tax,description:l10n_ch.1_vat_38_incl -#: model:account.tax.template,description:l10n_ch.vat_38_incl -msgid "3.8% Incl." -msgstr "3.8% Inkl." +#: model:account.tax.template,description:l10n_ch.vat_37_incl +msgid "3.7% Incl." +msgstr "3.7% Inkl." #. module: l10n_ch -#: model:account.tax,description:l10n_ch.1_vat_38_purchase -#: model:account.tax.template,description:l10n_ch.vat_38_purchase -msgid "3.8% achat" -msgstr "3.8% Einkauf" +#: model:account.tax.template,description:l10n_ch.vat_37_purchase +msgid "3.7% achat" +msgstr "3.7% Einkauf" #. module: l10n_ch -#: model:account.tax,description:l10n_ch.1_vat_38_purchase_incl -#: model:account.tax.template,description:l10n_ch.vat_38_purchase_incl -msgid "3.8% achat Incl." -msgstr "3.8% Einkauf Inkl." +#: model:account.tax.template,description:l10n_ch.vat_37_purchase_incl +msgid "3.7% achat Incl." +msgstr "3.7% Einkauf Inkl." #. module: l10n_ch -#: model:account.tax,description:l10n_ch.1_vat_38_invest -#: model:account.tax.template,description:l10n_ch.vat_38_invest -msgid "3.8% invest" -msgstr "3.8% Invest." +#: model:account.tax.template,description:l10n_ch.vat_37_invest +msgid "3.7% invest" +msgstr "3.7% Invest." #. module: l10n_ch -#: model:account.tax,description:l10n_ch.1_vat_38_invest_incl -#: model:account.tax.template,description:l10n_ch.vat_38_invest_incl -msgid "3.8% invest Incl." -msgstr "3.8% Invest. Inkl." +#: model:account.tax.template,description:l10n_ch.vat_37_invest_incl +msgid "3.7% invest Incl." +msgstr "3.7% Invest. Inkl." #. module: l10n_ch -#: model:account.tax,description:l10n_ch.1_vat_80 -#: model:account.tax.template,description:l10n_ch.vat_80 -msgid "8.0%" -msgstr "8.0%" +#: model:account.tax.template,description:l10n_ch.vat_77 +msgid "7.7%" +msgstr "7.7%" #. module: l10n_ch -#: model:account.tax,description:l10n_ch.1_vat_80_incl -#: model:account.tax.template,description:l10n_ch.vat_80_incl -msgid "8.0% Incl." -msgstr "8.0% Inkl." +#: model:account.tax.template,description:l10n_ch.vat_77_incl +msgid "7.7% Incl." +msgstr "7.7% Inkl." #. module: l10n_ch -#: model:account.tax,description:l10n_ch.1_vat_80_purchase -#: model:account.tax.template,description:l10n_ch.vat_80_purchase -msgid "8.0% achat" -msgstr "8.0% Einkauf" +#: model:account.tax.template,description:l10n_ch.vat_77_purchase +#: model:account.tax.template,description:l10n_ch.vat_77_purchase_reverse +msgid "7.7% achat" +msgstr "7.7% Einkauf" #. module: l10n_ch -#: model:account.tax,description:l10n_ch.1_vat_80_purchase_incl -#: model:account.tax.template,description:l10n_ch.vat_80_purchase_incl -msgid "8.0% achat Incl." -msgstr "8.0% Einkauf Inkl." +#: model:account.tax.template,description:l10n_ch.vat_77_purchase_return +msgid "7.7% achat (return)" +msgstr "" #. module: l10n_ch -#: model:account.tax,description:l10n_ch.1_vat_80_invest -#: model:account.tax.template,description:l10n_ch.vat_80_invest -msgid "8.0% invest." -msgstr "8.0% Invest." +#: model:account.tax.template,description:l10n_ch.vat_77_purchase_incl +msgid "7.7% achat Incl." +msgstr "7.7% Einkauf Inkl." #. module: l10n_ch -#: model:account.tax,description:l10n_ch.1_vat_80_invest_incl -#: model:account.tax.template,description:l10n_ch.vat_80_invest_incl -msgid "8.0% invest. Incl." -msgstr "8.0% Invest. Inkl." +#: model:account.tax.template,description:l10n_ch.vat_77_invest +msgid "7.7% invest." +msgstr "7.7% Invest." + +#. module: l10n_ch +#: model:account.tax.template,description:l10n_ch.vat_77_invest_incl +msgid "7.7% invest. Incl." +msgstr "7.7% Invest. Inkl." #. module: l10n_ch #: model:account.account.template,name:l10n_ch.ch_coa_4200 @@ -255,7 +241,8 @@ msgstr "Abschreibungen auf Fahrzeuge" #. module: l10n_ch #: model:account.account.template,name:l10n_ch.ch_coa_1579 msgid "Amortissements sur les équipements et installations" -msgstr "Abschreibungen und Wertberichtigungen feste Einrichtungen und Installationen" +msgstr "" +"Abschreibungen und Wertberichtigungen feste Einrichtungen und Installationen" #. module: l10n_ch #: model:account.account.template,name:l10n_ch.ch_coa_2270 @@ -312,37 +299,6 @@ msgstr "Übrige Erlöse aus Lieferungen und Leistungen" msgid "Avances et prêts" msgstr "Vorschüsse und Darlehen" -#. module: l10n_ch -#: model:ir.model,name:l10n_ch.model_res_bank -msgid "Bank" -msgstr "Bank" - -#. module: l10n_ch -#: model:ir.model,name:l10n_ch.model_res_partner_bank -msgid "Bank Accounts" -msgstr "Bankkonten" - -#. module: l10n_ch -#: model:ir.model.fields,help:l10n_ch.field_account_config_settings_l10n_ch_isr_print_bank_location -#: model:ir.model.fields,help:l10n_ch.field_res_company_l10n_ch_isr_print_bank_location -msgid "" -"Boolean option field indicating whether or not the alternate layout (the one " -"printing bank name and address) must be used when generating an ISR." -msgstr "" - -#. module: l10n_ch -#: model:ir.model.fields,help:l10n_ch.field_account_invoice_l10n_ch_isr_sent -msgid "" -"Boolean value telling whether or not the ISR corresponding to this invoice " -"has already been printed or sent by mail." -msgstr "" - -#. module: l10n_ch -#: model:ir.model.fields,help:l10n_ch.field_account_invoice_l10n_ch_isr_valid -msgid "" -"Boolean value. True iff all the data required to generate the ISR are present" -msgstr "" - #. module: l10n_ch #: model:account.account.template,name:l10n_ch.ch_coa_1700 msgid "Brevets, know-how, licences, droits, développement" @@ -358,11 +314,6 @@ msgstr "Jahresgewinn oder Jahresverlust" msgid "Bénéfice / perte reporté" msgstr "Gewinnvortrag oder Verlustvortrag" -#. module: l10n_ch -#: model:ir.model.fields,field_description:l10n_ch.field_res_bank_l10n_ch_postal_chf -msgid "CHF ISR reference" -msgstr "CHF ESR Referenznummer" - #. module: l10n_ch #: model:account.account.template,name:l10n_ch.ch_coa_1850 msgid "" @@ -520,11 +471,6 @@ msgstr "Forderungen gegenüber Sozialversicherungen und Vorsorgeeinrichtungen" msgid "Créanciers" msgstr "Verbindlichkeiten aus Lieferungen und Leistungen (Kreditoren)" -#. module: l10n_ch -#: model:ir.model.fields,field_description:l10n_ch.field_account_invoice_l10n_ch_currency_name -msgid "Currency" -msgstr "Währung" - #. module: l10n_ch #: model:account.account.template,name:l10n_ch.ch_coa_2100 #: model:account.account.template,name:l10n_ch.ch_coa_2400 @@ -577,11 +523,6 @@ msgstr "Einkaufsrabatte" msgid "Déductions sur ventes" msgstr "Erlösminderungen" -#. module: l10n_ch -#: model:ir.model.fields,field_description:l10n_ch.field_res_bank_l10n_ch_postal_eur -msgid "EUR ISR reference" -msgstr "" - #. module: l10n_ch #: model:account.account.template,name:l10n_ch.ch_coa_4540 msgid "Eau" @@ -597,11 +538,6 @@ msgstr "Strom" msgid "Email Templates" msgstr "" -#. module: l10n_ch -#: model:ir.model,name:l10n_ch.model_mail_compose_message -msgid "Email composition wizard" -msgstr "" - #. module: l10n_ch #: model:account.account.template,name:l10n_ch.ch_coa_2450 msgid "Emprunts" @@ -676,34 +612,6 @@ msgstr "Goodwill" msgid "Hypothèques" msgstr "Hypotheken" -#. module: l10n_ch -#: model:ir.actions.report.xml,name:l10n_ch.l10n_ch_isr_report -msgid "ISR" -msgstr "" - -#. module: l10n_ch -#: model_terms:ir.ui.view,arch_db:l10n_ch.l10n_ch_isr_report_template -msgid "ISR for invoice" -msgstr "" - -#. module: l10n_ch -#: model:ir.model.fields,help:l10n_ch.field_account_invoice_l10n_ch_isr_number_spaced -msgid "" -"ISR number split in blocks of 5 characters (right-justified), to generate " -"ISR report." -msgstr "" - -#. module: l10n_ch -#: model_terms:ir.ui.view,arch_db:l10n_ch.isr_invoice_search_view -msgid "ISR reference number" -msgstr "" - -#. module: l10n_ch -#: code:addons/l10n_ch/models/mail_compose_message.py:23 -#, python-format -msgid "ISR sent" -msgstr "" - #. module: l10n_ch #: model:account.account.template,name:l10n_ch.ch_coa_1600 msgid "Immeubles d’exploitation" @@ -751,51 +659,6 @@ msgstr "Direkte Steuern" msgid "Installations de stockage" msgstr "Installations de stockage" -#. module: l10n_ch -#: model:ir.model,name:l10n_ch.model_account_invoice -msgid "Invoice" -msgstr "" - -#. module: l10n_ch -#: model:ir.model.fields,field_description:l10n_ch.field_account_invoice_l10n_ch_isr_number -msgid "L10N Ch Isr Number" -msgstr "" - -#. module: l10n_ch -#: model:ir.model.fields,field_description:l10n_ch.field_account_invoice_l10n_ch_isr_number_spaced -msgid "L10N Ch Isr Number Spaced" -msgstr "" - -#. module: l10n_ch -#: model:ir.model.fields,field_description:l10n_ch.field_account_invoice_l10n_ch_isr_optical_line -msgid "L10N Ch Isr Optical Line" -msgstr "" - -#. module: l10n_ch -#: model:ir.model.fields,field_description:l10n_ch.field_account_invoice_l10n_ch_isr_postal -msgid "L10N Ch Isr Postal" -msgstr "" - -#. module: l10n_ch -#: model:ir.model.fields,field_description:l10n_ch.field_account_invoice_l10n_ch_isr_postal_formatted -msgid "L10N Ch Isr Postal Formatted" -msgstr "" - -#. module: l10n_ch -#: model:ir.model.fields,field_description:l10n_ch.field_account_invoice_l10n_ch_isr_sent -msgid "L10N Ch Isr Sent" -msgstr "" - -#. module: l10n_ch -#: model:ir.model.fields,field_description:l10n_ch.field_account_invoice_l10n_ch_isr_valid -msgid "L10N Ch Isr Valid" -msgstr "" - -#. module: l10n_ch -#: model:ir.model.fields,field_description:l10n_ch.field_res_partner_bank_l10n_ch_postal -msgid "L10N Ch Postal" -msgstr "" - #. module: l10n_ch #: model:account.account.template,name:l10n_ch.ch_coa_6260 msgid "Leasing et location de véhicules" @@ -806,6 +669,11 @@ msgstr "Fahrzeugleasing und -mieten" msgid "Leasing immobilisations corporelles meubles" msgstr "Leasingaufwand mobile Sachanlagen" +#. module: l10n_ch +#: model:account.account.template,name:l10n_ch.l10nch_chart_template_liquidity_transfer +msgid "Liquidity Transfer" +msgstr "" + #. module: l10n_ch #: model:account.account.template,name:l10n_ch.ch_coa_1520 msgid "Machines de bureau, informatique, systèmes de communication" @@ -851,11 +719,6 @@ msgstr "Heizöl" msgid "Mobilier et installations" msgstr "Mobiliar und Einrichtungen" -#. module: l10n_ch -#: model:ir.model.fields,help:l10n_ch.field_account_invoice_l10n_ch_isr_optical_line -msgid "Optical reading line, as it will be printed on ISR" -msgstr "" - #. module: l10n_ch #: model:account.account.template,name:l10n_ch.ch_coa_1540 msgid "Outillages et appareils" @@ -881,13 +744,6 @@ msgstr "Verluste Forderungen (Debitoren), Veränderung Delkredere" msgid "Plan comptable 2015" msgstr "Kontenrahmen 2015" -#. module: l10n_ch -#: model:ir.model.fields,help:l10n_ch.field_account_invoice_l10n_ch_isr_postal_formatted -msgid "" -"Postal reference of the bank, formated with '-' and without the padding " -"zeros, to generate ISR report." -msgstr "" - #. module: l10n_ch #: model:account.account.template,name:l10n_ch.ch_coa_4400 msgid "Prestations / travaux de tiers" @@ -898,30 +754,6 @@ msgstr "Aufwand für bezogene Dienstleistungen" msgid "Prestations propres" msgstr "Eigenleistungen" -#. module: l10n_ch -#: model_terms:ir.ui.view,arch_db:l10n_ch.isr_invoice_form -msgid "Print ISR" -msgstr "" - -#. module: l10n_ch -#: model:ir.model.fields,field_description:l10n_ch.field_res_company_l10n_ch_isr_print_bank_location -msgid "Print bank location" -msgstr "" - -#. module: l10n_ch -#: model:ir.model.fields,field_description:l10n_ch.field_account_config_settings_l10n_ch_isr_print_bank_location -msgid "Print bank on ISR" -msgstr "" - -#. module: l10n_ch -#: model_terms:ir.ui.view,arch_db:l10n_ch.l10n_ch-isr_account_config_settings -msgid "" -"Print the coordinates of your bank under the 'Payment for' title of the " -"ISR.\n" -" Your address will be moved to the 'in favour " -"of' section." -msgstr "" - #. module: l10n_ch #: model:account.account.template,name:l10n_ch.ch_coa_7000 msgid "Produits accessoires" @@ -1034,11 +866,6 @@ msgstr "Fertige Erzeugnisse" msgid "Stocks de produits semi-ouvrés" msgstr "" -#. module: l10n_ch -#: model:ir.ui.menu,name:l10n_ch.account_reports_ch_statements_menu -msgid "Swiss Statements" -msgstr "CH Bilanz- und Erfolgsrechnungen" - #. module: l10n_ch #: model:account.account.tag,name:l10n_ch.vat_tag_dedouanement msgid "Switzerland VAT Form: 100% dédouanement (base in grid 400)" @@ -1080,13 +907,13 @@ msgid "Switzerland VAT Form: grid 280" msgstr "" #. module: l10n_ch -#: model:account.account.tag,name:l10n_ch.vat_tag_301_a -msgid "Switzerland VAT Form: grid 301 base" +#: model:account.account.tag,name:l10n_ch.vat_tag_302_a +msgid "Switzerland VAT Form: grid 302 base" msgstr "" #. module: l10n_ch -#: model:account.account.tag,name:l10n_ch.vat_tag_301_b -msgid "Switzerland VAT Form: grid 301 tax" +#: model:account.account.tag,name:l10n_ch.vat_tag_302_b +msgid "Switzerland VAT Form: grid 302 tax" msgstr "" #. module: l10n_ch @@ -1100,13 +927,13 @@ msgid "Switzerland VAT Form: grid 311 tax" msgstr "" #. module: l10n_ch -#: model:account.account.tag,name:l10n_ch.vat_tag_341_a -msgid "Switzerland VAT Form: grid 341 base" +#: model:account.account.tag,name:l10n_ch.vat_tag_342_a +msgid "Switzerland VAT Form: grid 342 base" msgstr "" #. module: l10n_ch -#: model:account.account.tag,name:l10n_ch.vat_tag_341_b -msgid "Switzerland VAT Form: grid 341 tax" +#: model:account.account.tag,name:l10n_ch.vat_tag_342_b +msgid "Switzerland VAT Form: grid 342 tax" msgstr "" #. module: l10n_ch @@ -1119,6 +946,16 @@ msgstr "" msgid "Switzerland VAT Form: grid 381 tax" msgstr "" +#. module: l10n_ch +#: model:account.account.tag,name:l10n_ch.vat_tag_382_a +msgid "Switzerland VAT Form: grid 382 base" +msgstr "" + +#. module: l10n_ch +#: model:account.account.tag,name:l10n_ch.vat_tag_382_b +msgid "Switzerland VAT Form: grid 382 tax" +msgstr "" + #. module: l10n_ch #: model:account.account.tag,name:l10n_ch.vat_tag_400 msgid "Switzerland VAT Form: grid 400" @@ -1144,6 +981,11 @@ msgstr "" msgid "Switzerland VAT Form: grid 420" msgstr "" +#. module: l10n_ch +#: model:account.tax.group,name:l10n_ch.tax_group_tva_0 +msgid "TVA 0%" +msgstr "MwSt. 0%" + #. module: l10n_ch #: model:account.tax.template,name:l10n_ch.vat_O_import msgid "TVA 0% Importations de biens et services" @@ -1154,6 +996,11 @@ msgstr "MwSt. 0% auf Importe von Gütern und Dienstleistungen" msgid "TVA 0% exclue" msgstr "MwSt. 0% ausgeschlossen" +#. module: l10n_ch +#: model:account.tax.group,name:l10n_ch.tax_group_tva_25 +msgid "TVA 2.5%" +msgstr "MwSt. 2.5%" + #. module: l10n_ch #: model:account.tax.template,name:l10n_ch.vat_25_purchase_incl msgid "TVA 2.5% sur achat B&S (Incl. TR)" @@ -1175,44 +1022,59 @@ msgid "TVA 2.5% sur invest. et autres ch. (TR)" msgstr "MwSt. 2,5% auf Invest. und sonst. Aufw. (USt)" #. module: l10n_ch -#: model:account.tax.template,name:l10n_ch.vat_38_purchase_incl -msgid "TVA 3.8% sur achat B&S (Incl. TS)" -msgstr "MwSt. 3,8% auf Einkäufe B&S (inkl. LSt)" +#: model:account.tax.group,name:l10n_ch.tax_group_tva_37 +msgid "TVA 3.7%" +msgstr "MwSt. 3,7%" #. module: l10n_ch -#: model:account.tax.template,name:l10n_ch.vat_38_purchase -msgid "TVA 3.8% sur achat B&S (TS)" -msgstr "MwSt. 3,8% auf Einkäufe B&S (LSt)" +#: model:account.tax.template,name:l10n_ch.vat_37_purchase_incl +msgid "TVA 3.7% sur achat B&S (Incl. TS)" +msgstr "MwSt. 3,7% auf Einkäufe B&S (inkl. LSt)" #. module: l10n_ch -#: model:account.tax.template,name:l10n_ch.vat_38_invest_incl -msgid "TVA 3.8% sur invest. et autres ch. (Incl. TS)" -msgstr "MwSt. 3,8% auf Invest. und sonst. Aufw. (inkl. LSt)" +#: model:account.tax.template,name:l10n_ch.vat_37_purchase +msgid "TVA 3.7% sur achat B&S (TS)" +msgstr "MwSt. 3,7% auf Einkäufe B&S (LSt)" #. module: l10n_ch -#: model:account.tax.template,name:l10n_ch.vat_38_invest -msgid "TVA 3.8% sur invest. et autres ch. (TS)" -msgstr "MwSt. 3,8% auf Invest. und sonst. Aufw. (LSt)" +#: model:account.tax.template,name:l10n_ch.vat_37_invest_incl +msgid "TVA 3.7% sur invest. et autres ch. (Incl. TS)" +msgstr "MwSt. 3,7% auf Invest. und sonst. Aufw. (inkl. LSt)" #. module: l10n_ch -#: model:account.tax.template,name:l10n_ch.vat_80_purchase_incl -msgid "TVA 8.0% sur achat B&S (Incl. TN)" -msgstr "MwSt. 8.0% auf Einkäufe B&S (inkl. NRZ)" +#: model:account.tax.template,name:l10n_ch.vat_37_invest +msgid "TVA 3.7% sur invest. et autres ch. (TS)" +msgstr "MwSt. 3,7% auf Invest. und sonst. Aufw. (LSt)" #. module: l10n_ch -#: model:account.tax.template,name:l10n_ch.vat_80_purchase -msgid "TVA 8.0% sur achat B&S (TN)" -msgstr "MwSt. 8,0% auf Einkäufe B&S (NRZ)" +#: model:account.tax.group,name:l10n_ch.tax_group_tva_77 +msgid "TVA 7.7%" +msgstr "MwSt. 7,7%" #. module: l10n_ch -#: model:account.tax.template,name:l10n_ch.vat_80_invest_incl -msgid "TVA 8.0% sur invest. et autres ch. (Incl. TN)" -msgstr "MwSt. 8,0% auf Invest. und sonst. Aufw. (inkl. NRZ)" +#: model:account.tax.template,name:l10n_ch.vat_77_purchase_incl +msgid "TVA 7.7% sur achat B&S (Incl. TN)" +msgstr "MwSt. 7,7% auf Einkäufe B&S (inkl. NRZ)" #. module: l10n_ch -#: model:account.tax.template,name:l10n_ch.vat_80_invest -msgid "TVA 8.0% sur invest. et autres ch. (TN)" -msgstr "MwSt. 8,0% auf Invest. und sonst. Aufw. (NRZ)" +#: model:account.tax.template,name:l10n_ch.vat_77_purchase +msgid "TVA 7.7% sur achat B&S (TN)" +msgstr "MwSt. 7,7% auf Einkäufe B&S (NRZ)" + +#. module: l10n_ch +#: model:account.tax.template,name:l10n_ch.vat_77_purchase_reverse +msgid "TVA 7.7% sur achat service a l'etranger (reverse charge)" +msgstr "" + +#. module: l10n_ch +#: model:account.tax.template,name:l10n_ch.vat_77_invest_incl +msgid "TVA 7.7% sur invest. et autres ch. (Incl. TN)" +msgstr "MwSt. 7,7% auf Invest. und sonst. Aufw. (inkl. NRZ)" + +#. module: l10n_ch +#: model:account.tax.template,name:l10n_ch.vat_77_invest +msgid "TVA 7.7% sur invest. et autres ch. (TN)" +msgstr "MwSt. 7,7% auf Invest. und sonst. Aufw. (NRZ)" #. module: l10n_ch #: model:account.account.template,name:l10n_ch.ch_coa_2200 @@ -1230,61 +1092,34 @@ msgid "TVA due a 2.5% (TR)" msgstr "Zu zahlende MwSt. bei 2,5% (USt)" #. module: l10n_ch -#: model:account.tax.template,name:l10n_ch.vat_38 -msgid "TVA due a 3.8% (TS)" -msgstr "Zu zahlende MwSt. bei 3,8% (LSt)" +#: model:account.tax.template,name:l10n_ch.vat_37 +msgid "TVA due a 3.7% (TS)" +msgstr "Zu zahlende MwSt. bei 3,7% (LSt)" #. module: l10n_ch -#: model:account.tax.template,name:l10n_ch.vat_80 -msgid "TVA due a 8.0% (TN)" +#: model:account.tax.template,name:l10n_ch.vat_77 +msgid "TVA due a 7.7% (TN)" msgstr "Zu zahlende MwSt. bei 8,0% (NRZ)" +#. module: l10n_ch +#: model:account.tax.template,name:l10n_ch.vat_77_purchase_return +msgid "TVA due a 7.7% (TN) (return)" +msgstr "" + #. module: l10n_ch #: model:account.tax.template,name:l10n_ch.vat_25_incl msgid "TVA due à 2.5% (Incl. TR)" msgstr "Zu zahlende MwSt. bei 2,5% (inkl. USt)" #. module: l10n_ch -#: model:account.tax.template,name:l10n_ch.vat_38_incl -msgid "TVA due à 3.8% (Incl. TS)" -msgstr "Zu zahlende MwSt. bei 3,8% (inkl. LSt)" +#: model:account.tax.template,name:l10n_ch.vat_37_incl +msgid "TVA due à 3.7% (Incl. TS)" +msgstr "Zu zahlende MwSt. bei 3,7% (inkl. LSt)" #. module: l10n_ch -#: model:account.tax.template,name:l10n_ch.vat_80_incl -msgid "TVA due à 8.0% (Incl. TN)" -msgstr "Zu zahlende MwSt. bei 8,0% (inkl. NRZ)" - -#. module: l10n_ch -#: model:ir.model.fields,help:l10n_ch.field_res_partner_bank_l10n_ch_postal -msgid "The ISR number of the company within the bank" -msgstr "" - -#. module: l10n_ch -#: model:ir.model.fields,help:l10n_ch.field_account_invoice_l10n_ch_currency_name -msgid "The name of this invoice's currency" -msgstr "" - -#. module: l10n_ch -#: model:ir.model.fields,help:l10n_ch.field_account_invoice_l10n_ch_isr_postal -msgid "The postal reference identifying the bank managing this ISR." -msgstr "" - -#. module: l10n_ch -#: model:ir.model.fields,help:l10n_ch.field_res_bank_l10n_ch_postal_chf -msgid "" -"The postal reference of the bank, used to generate ISR payment slips in CHF." -msgstr "" - -#. module: l10n_ch -#: model:ir.model.fields,help:l10n_ch.field_res_bank_l10n_ch_postal_eur -msgid "" -"The postal reference of the bank, used to generate ISR payment slips in EUR." -msgstr "" - -#. module: l10n_ch -#: model:ir.model.fields,help:l10n_ch.field_account_invoice_l10n_ch_isr_number -msgid "The reference number associated with this invoice" -msgstr "" +#: model:account.tax.template,name:l10n_ch.vat_77_incl +msgid "TVA due à 7.7% (Incl. TN)" +msgstr "Zu zahlende MwSt. bei 7,7% (inkl. NRZ)" #. module: l10n_ch #: model:account.account.template,name:l10n_ch.ch_coa_1060 @@ -1296,11 +1131,6 @@ msgstr "Wertschriften" msgid "Titres à long terme" msgstr "Wertschriften" -#. module: l10n_ch -#: model:account.account.template,name:l10n_ch.transfer_account_id -msgid "Transferts internes" -msgstr "Interne Verrechnungen" - #. module: l10n_ch #: model:account.account.template,name:l10n_ch.ch_coa_1280 msgid "Travaux en cours" @@ -1377,30 +1207,3 @@ msgstr "Produktionserlöse" #: model:account.account.template,name:l10n_ch.ch_coa_1530 msgid "Véhicules" msgstr "Fahrzeuge" - -#. module: l10n_ch -#: code:addons/l10n_ch/models/account_invoice.py:153 -#, python-format -msgid "" -"You cannot generate an ISR yet.\n" -"\n" -" For this, you need to :\n" -"\n" -" - set a valid postal account number (or " -"an IBAN referencing one) for your company\n" -"\n" -" - define its bank\n" -"\n" -" - associate this bank with a postal " -"reference for the currency used in this invoice\n" -"\n" -" - fill the 'bank account' field of the " -"invoice with the postal to be used to receive the related payment. A default " -"account will be automatically set for all invoices created after you defined " -"a postal account for your company." -msgstr "" - -#. module: l10n_ch -#: model:ir.model,name:l10n_ch.model_account_config_settings -msgid "account.config.settings" -msgstr "" diff --git a/addons/l10n_ch/i18n_extra/it.po b/addons/l10n_ch/i18n_extra/it.po index 820569f207b..2af6ef38dc0 100644 --- a/addons/l10n_ch/i18n_extra/it.po +++ b/addons/l10n_ch/i18n_extra/it.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: Odoo Server 8.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-03-14 15:52+0000\n" +"POT-Creation-Date: 2019-02-12 05:39+0000\n" "PO-Revision-Date: 2014-12-02 09:47+0000\n" "Last-Translator: <>\n" "Language-Team: \n" @@ -17,201 +17,173 @@ msgstr "" "Plural-Forms: \n" #. module: l10n_ch -#: model:account.tax,description:l10n_ch.1_vat_O_exclude #: model:account.tax.template,description:l10n_ch.vat_O_exclude msgid "0% excl." msgstr "" #. module: l10n_ch -#: model:account.tax,description:l10n_ch.1_vat_O_import #: model:account.tax.template,description:l10n_ch.vat_O_import msgid "0% import." msgstr "" #. module: l10n_ch -#: model:account.tax,name:l10n_ch.1_vat_100_import #: model:account.tax.template,name:l10n_ch.vat_100_import msgid "100% dédouanement TVA" msgstr "" #. module: l10n_ch -#: model:account.tax,description:l10n_ch.1_vat_100_import #: model:account.tax.template,description:l10n_ch.vat_100_import msgid "100% imp." msgstr "" #. module: l10n_ch -#: model:account.tax,description:l10n_ch.1_vat_25 #: model:account.tax.template,description:l10n_ch.vat_25 msgid "2.5%" msgstr "" #. module: l10n_ch -#: model:account.tax,description:l10n_ch.1_vat_25_incl #: model:account.tax.template,description:l10n_ch.vat_25_incl msgid "2.5% Incl." msgstr "" #. module: l10n_ch -#: model:account.tax,description:l10n_ch.1_vat_25_purchase #: model:account.tax.template,description:l10n_ch.vat_25_purchase msgid "2.5% achat" msgstr "" #. module: l10n_ch -#: model:account.tax,description:l10n_ch.1_vat_25_purchase_incl #: model:account.tax.template,description:l10n_ch.vat_25_purchase_incl msgid "2.5% achat Incl." msgstr "" #. module: l10n_ch -#: model:account.tax,description:l10n_ch.1_vat_25_invest #: model:account.tax.template,description:l10n_ch.vat_25_invest msgid "2.5% invest." msgstr "" #. module: l10n_ch -#: model:account.tax,description:l10n_ch.1_vat_25_invest_incl #: model:account.tax.template,description:l10n_ch.vat_25_invest_incl msgid "2.5% invest. Incl." msgstr "" #. module: l10n_ch -#: model:account.tax,description:l10n_ch.1_vat_38 -#: model:account.tax.template,description:l10n_ch.vat_38 -msgid "3.8%" +#: model:account.tax.template,description:l10n_ch.vat_37 +msgid "3.7%" msgstr "" #. module: l10n_ch -#: model:account.tax,description:l10n_ch.1_vat_38_incl -#: model:account.tax.template,description:l10n_ch.vat_38_incl -msgid "3.8% Incl." +#: model:account.tax.template,description:l10n_ch.vat_37_incl +msgid "3.7% Incl." msgstr "" #. module: l10n_ch -#: model:account.tax,description:l10n_ch.1_vat_38_purchase -#: model:account.tax.template,description:l10n_ch.vat_38_purchase -msgid "3.8% achat" +#: model:account.tax.template,description:l10n_ch.vat_37_purchase +msgid "3.7% achat" msgstr "" #. module: l10n_ch -#: model:account.tax,description:l10n_ch.1_vat_38_purchase_incl -#: model:account.tax.template,description:l10n_ch.vat_38_purchase_incl -msgid "3.8% achat Incl." +#: model:account.tax.template,description:l10n_ch.vat_37_purchase_incl +msgid "3.7% achat Incl." msgstr "" #. module: l10n_ch -#: model:account.tax,description:l10n_ch.1_vat_38_invest -#: model:account.tax.template,description:l10n_ch.vat_38_invest -msgid "3.8% invest" +#: model:account.tax.template,description:l10n_ch.vat_37_invest +msgid "3.7% invest" msgstr "" #. module: l10n_ch -#: model:account.tax,description:l10n_ch.1_vat_38_invest_incl -#: model:account.tax.template,description:l10n_ch.vat_38_invest_incl -msgid "3.8% invest Incl." +#: model:account.tax.template,description:l10n_ch.vat_37_invest_incl +msgid "3.7% invest Incl." msgstr "" #. module: l10n_ch -#: model:account.tax,description:l10n_ch.1_vat_80 -#: model:account.tax.template,description:l10n_ch.vat_80 -msgid "8.0%" +#: model:account.tax.template,description:l10n_ch.vat_77 +msgid "7.7%" msgstr "" #. module: l10n_ch -#: model:account.tax,description:l10n_ch.1_vat_80_incl -#: model:account.tax.template,description:l10n_ch.vat_80_incl -msgid "8.0% Incl." +#: model:account.tax.template,description:l10n_ch.vat_77_incl +msgid "7.7% Incl." msgstr "" #. module: l10n_ch -#: model:account.tax,description:l10n_ch.1_vat_80_purchase -#: model:account.tax.template,description:l10n_ch.vat_80_purchase -msgid "8.0% achat" +#: model:account.tax.template,description:l10n_ch.vat_77_purchase +#: model:account.tax.template,description:l10n_ch.vat_77_purchase_reverse +msgid "7.7% achat" msgstr "" #. module: l10n_ch -#: model:account.tax,description:l10n_ch.1_vat_80_purchase_incl -#: model:account.tax.template,description:l10n_ch.vat_80_purchase_incl -msgid "8.0% achat Incl." +#: model:account.tax.template,description:l10n_ch.vat_77_purchase_return +msgid "7.7% achat (return)" msgstr "" #. module: l10n_ch -#: model:account.tax,description:l10n_ch.1_vat_80_invest -#: model:account.tax.template,description:l10n_ch.vat_80_invest -msgid "8.0% invest." +#: model:account.tax.template,description:l10n_ch.vat_77_purchase_incl +msgid "7.7% achat Incl." msgstr "" #. module: l10n_ch -#: model:account.tax,description:l10n_ch.1_vat_80_invest_incl -#: model:account.tax.template,description:l10n_ch.vat_80_invest_incl -msgid "8.0% invest. Incl." +#: model:account.tax.template,description:l10n_ch.vat_77_invest +msgid "7.7% invest." +msgstr "" + +#. module: l10n_ch +#: model:account.tax.template,description:l10n_ch.vat_77_invest_incl +msgid "7.7% invest. Incl." msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_4200 #: model:account.account.template,name:l10n_ch.ch_coa_4200 msgid "Achats de marchandises destinées à la revente" msgstr "Costi delle merci di rivendita" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_2030 #: model:account.account.template,name:l10n_ch.ch_coa_2030 msgid "Acomptes de clients" msgstr "Acconti ricevuti" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1208 #: model:account.account.template,name:l10n_ch.ch_coa_1208 msgid "Acomptes sur les marchandises commerciales" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1218 #: model:account.account.template,name:l10n_ch.ch_coa_1218 msgid "Acomptes sur matières premières" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1149 #: model:account.account.template,name:l10n_ch.ch_coa_1149 msgid "Ajustement de la valeur des avances et des prêts" msgstr "Rettifica valore anticipi e prestiti" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1199 #: model:account.account.template,name:l10n_ch.ch_coa_1199 msgid "Ajustement de la valeur des créances à court terme" msgstr "Rettifica valore crediti diversi a breve termine" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1449 #: model:account.account.template,name:l10n_ch.ch_coa_1449 msgid "Ajustement de la valeur des créances à long terme" msgstr "Rettifica valore crediti a lungo termine" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1779 #: model:account.account.template,name:l10n_ch.ch_coa_1779 msgid "Ajustement de la valeur des goodwill" msgstr "Rettifica valore goodwill" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1489 #: model:account.account.template,name:l10n_ch.ch_coa_1489 msgid "Ajustement de la valeur des participations" msgstr "Rettifica valore partecipazioni" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1069 -#: model:account.account,name:l10n_ch.1_ch_coa_1409 #: model:account.account.template,name:l10n_ch.ch_coa_1069 #: model:account.account.template,name:l10n_ch.ch_coa_1409 msgid "Ajustement de la valeur des titres" msgstr "Rettifica valore titoli" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_6800 #: model:account.account.template,name:l10n_ch.ch_coa_6800 msgid "" "Amortissements et ajustements de valeur des postes sur immobilisations " @@ -219,187 +191,126 @@ msgid "" msgstr "Ammortamenti e rettifiche di valore dell’attivo fisso" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1519 #: model:account.account.template,name:l10n_ch.ch_coa_1519 msgid "Amortissements sur le mobilier et les installations" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1599 #: model:account.account.template,name:l10n_ch.ch_coa_1599 msgid "Amortissements sur les autres immobilisations corporelles meubles" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1709 #: model:account.account.template,name:l10n_ch.ch_coa_1709 msgid "Amortissements sur les brevets, know-how, licences, droits, dév." msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1609 #: model:account.account.template,name:l10n_ch.ch_coa_1609 msgid "Amortissements sur les immeubles d’exploitation" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1559 #: model:account.account.template,name:l10n_ch.ch_coa_1559 msgid "Amortissements sur les installations de stockage" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1529 #: model:account.account.template,name:l10n_ch.ch_coa_1529 msgid "Amortissements sur les machines de bureau, inf. et syst. comm." msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1509 #: model:account.account.template,name:l10n_ch.ch_coa_1509 msgid "Amortissements sur les machines et appareils" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1549 #: model:account.account.template,name:l10n_ch.ch_coa_1549 msgid "Amortissements sur les outillages et appareils" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1539 #: model:account.account.template,name:l10n_ch.ch_coa_1539 msgid "Amortissements sur les véhicules" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1579 #: model:account.account.template,name:l10n_ch.ch_coa_1579 msgid "Amortissements sur les équipements et installations" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_2270 #: model:account.account.template,name:l10n_ch.ch_coa_2270 msgid "Assurances sociales et institutions de prévoyance" msgstr "Assicurazioni sociali e istituti di previdenza" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_6300 #: model:account.account.template,name:l10n_ch.ch_coa_6300 msgid "Assurances-choses, droits, taxes, autorisations" msgstr "Assicurazioni cose, contributi, tasse e autorizzazioni" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_5800 #: model:account.account.template,name:l10n_ch.ch_coa_5800 msgid "Autres charges du personnel" msgstr "Altri costi del personale" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_6700 #: model:account.account.template,name:l10n_ch.ch_coa_6700 msgid "Autres charges d‘exploitation" msgstr "Altri costi d’esercizio" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1190 #: model:account.account.template,name:l10n_ch.ch_coa_1190 msgid "Autres créances à court terme" msgstr "Altri crediti a breve termine" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_2210 #: model:account.account.template,name:l10n_ch.ch_coa_2210 msgid "Autres dettes à court terme" msgstr "Altri debiti a breve termine" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_2140 #: model:account.account.template,name:l10n_ch.ch_coa_2140 msgid "Autres dettes à court terme rémunérées" msgstr "Altri debiti onerosi" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_2500 #: model:account.account.template,name:l10n_ch.ch_coa_2500 msgid "Autres dettes à long terme" msgstr "Altri debiti a lungo termine" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1590 #: model:account.account.template,name:l10n_ch.ch_coa_1590 msgid "Autres immobilisations corporelles meubles" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_3600 #: model:account.account.template,name:l10n_ch.ch_coa_3600 msgid "Autres ventes et prestations de services" msgstr "Altri ricavi e prestazioni di servizi" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1140 #: model:account.account.template,name:l10n_ch.ch_coa_1140 msgid "Avances et prêts" msgstr "Anticipi e prestiti" #. module: l10n_ch -#: model:ir.model,name:l10n_ch.model_res_bank -msgid "Bank" -msgstr "" - -#. module: l10n_ch -#: model:ir.model,name:l10n_ch.model_res_partner_bank -msgid "Bank Accounts" -msgstr "" - -#. module: l10n_ch -#: model:ir.model.fields,help:l10n_ch.field_account_config_settings_l10n_ch_isr_print_bank_location -#: model:ir.model.fields,help:l10n_ch.field_res_company_l10n_ch_isr_print_bank_location -msgid "" -"Boolean option field indicating whether or not the alternate layout (the one " -"printing bank name and address) must be used when generating an ISR." -msgstr "" - -#. module: l10n_ch -#: model:ir.model.fields,help:l10n_ch.field_account_invoice_l10n_ch_isr_sent -msgid "" -"Boolean value telling whether or not the ISR corresponding to this invoice " -"has already been printed or sent by mail." -msgstr "" - -#. module: l10n_ch -#: model:ir.model.fields,help:l10n_ch.field_account_invoice_l10n_ch_isr_valid -msgid "" -"Boolean value. True iff all the data required to generate the ISR are present" -msgstr "" - -#. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1700 #: model:account.account.template,name:l10n_ch.ch_coa_1700 msgid "Brevets, know-how, licences, droits, développement" msgstr "Patenti, know-how, licenze, diritti e sviluppo" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_2979 #: model:account.account.template,name:l10n_ch.ch_coa_2979 msgid "Bénéfice / perte de l’exercice" msgstr "Utile/perdita annuale" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_2970 #: model:account.account.template,name:l10n_ch.ch_coa_2970 msgid "Bénéfice / perte reporté" msgstr "Utile / perdita riportata" #. module: l10n_ch -#: model:ir.model.fields,field_description:l10n_ch.field_res_bank_l10n_ch_postal_chf -msgid "CHF ISR reference" -msgstr "" - -#. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1850 #: model:account.account.template,name:l10n_ch.ch_coa_1850 msgid "" "Capital actions, capital social, droits de participations ou capital de " @@ -409,7 +320,6 @@ msgstr "" "della fondazione non versati" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_2800 #: model:account.account.template,name:l10n_ch.ch_coa_2800 msgid "Capital-actions, capital social, capital de fondation" msgstr "" @@ -417,109 +327,91 @@ msgstr "" "della fondazione" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_4521 #: model:account.account.template,name:l10n_ch.ch_coa_4521 msgid "Charbon, briquettes, bois" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_7010 #: model:account.account.template,name:l10n_ch.ch_coa_7010 msgid "Charges accessoires" msgstr "Costi attività accessoria" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_6000 #: model:account.account.template,name:l10n_ch.ch_coa_6000 msgid "Charges de locaux" msgstr "Costi dei locali" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_4000 #: model:account.account.template,name:l10n_ch.ch_coa_4000 msgid "Charges de matériel de l‘atelier" msgstr "Costi materiale per la fabbricazione" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_5900 #: model:account.account.template,name:l10n_ch.ch_coa_5900 msgid "Charges de personnels temporaires" msgstr "Prestazioni di terz" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_6200 #: model:account.account.template,name:l10n_ch.ch_coa_6200 msgid "Charges de véhicules et de transport" msgstr "Costi auto e di trasporto" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_7510 #: model:account.account.template,name:l10n_ch.ch_coa_7510 msgid "Charges des immeubles d‘exploitation" msgstr "Costi immobili aziendali" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_6500 #: model:account.account.template,name:l10n_ch.ch_coa_6500 msgid "Charges d‘administration" msgstr "Costi amministrativi" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_6400 #: model:account.account.template,name:l10n_ch.ch_coa_6400 msgid "Charges d’énergie et évacuation des déchets" msgstr "Costi energia e smaltimento" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_6570 #: model:account.account.template,name:l10n_ch.ch_coa_6570 msgid "Charges et leasing d’informatique" msgstr "Costi informatici incluso leasing" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_8500 #: model:account.account.template,name:l10n_ch.ch_coa_8500 msgid "Charges extraordinaires, exceptionnelles ou hors période" msgstr "Costi straordinari, unici o relativi ad altri periodi contabili" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_6900 #: model:account.account.template,name:l10n_ch.ch_coa_6900 msgid "Charges financières" msgstr "Costi finanziari" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_8000 #: model:account.account.template,name:l10n_ch.ch_coa_8000 msgid "Charges hors exploitation" msgstr "Costi estranei" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1300 #: model:account.account.template,name:l10n_ch.ch_coa_1300 msgid "Charges payées d‘avance" msgstr "Costi anticipati" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_5700 #: model:account.account.template,name:l10n_ch.ch_coa_5700 msgid "Charges sociales" msgstr "Oneri sociali" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_2300 #: model:account.account.template,name:l10n_ch.ch_coa_2300 msgid "Charges à payer" msgstr "Costi da pagare" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_3803 #: model:account.account.template,name:l10n_ch.ch_coa_3803 msgid "Commissions de tiers" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_4903 #: model:account.account.template,name:l10n_ch.ch_coa_4903 msgid "Commissions obtenues sur achats" msgstr "" @@ -530,147 +422,113 @@ msgid "Companies" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1099 #: model:account.account.template,name:l10n_ch.ch_coa_1099 msgid "Compte d'attente autre" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1091 #: model:account.account.template,name:l10n_ch.ch_coa_1091 msgid "Compte d'attente pour salaires" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_3710 #: model:account.account.template,name:l10n_ch.ch_coa_3710 msgid "Consommations propres" msgstr "Consumo proprio" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1269 #: model:account.account.template,name:l10n_ch.ch_coa_1269 msgid "Correction de la valeur de stocks de produits finis" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1279 #: model:account.account.template,name:l10n_ch.ch_coa_1279 msgid "Corrections de la valeur des stock produits semi-ouvrés" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1209 #: model:account.account.template,name:l10n_ch.ch_coa_1209 msgid "Corrections de la valeur des stocks de marchandises" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1289 #: model:account.account.template,name:l10n_ch.ch_coa_1289 msgid "Corrections de la valeur des travaux en cours" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1219 #: model:account.account.template,name:l10n_ch.ch_coa_1219 msgid "Corrections de la valeur sur matières premières" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1180 #: model:account.account.template,name:l10n_ch.ch_coa_1180 msgid "Créances envers les assurances sociales et institutions de prévoyance" msgstr "Crediti da assicurazioni sociali e istituti di previdenza" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_2000 #: model:account.account.template,name:l10n_ch.ch_coa_2000 msgid "Créanciers" msgstr "Debiti per forniture e prestazioni (creditori)" #. module: l10n_ch -#: model:ir.model.fields,field_description:l10n_ch.field_account_invoice_l10n_ch_currency_name -msgid "Currency" -msgstr "" - -#. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_2100 -#: model:account.account,name:l10n_ch.1_ch_coa_2400 #: model:account.account.template,name:l10n_ch.ch_coa_2100 #: model:account.account.template,name:l10n_ch.ch_coa_2400 msgid "Dettes bancaires" msgstr "Banca passiva" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_2160 #: model:account.account.template,name:l10n_ch.ch_coa_2160 msgid "Dettes envers l'actionnaire" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_3806 -#: model:account.account,name:l10n_ch.1_ch_coa_4906 #: model:account.account.template,name:l10n_ch.ch_coa_3806 #: model:account.account.template,name:l10n_ch.ch_coa_4906 msgid "Différences de change" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_2261 #: model:account.account.template,name:l10n_ch.ch_coa_2261 msgid "Dividendes" msgstr "Dividendi" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_4071 #: model:account.account.template,name:l10n_ch.ch_coa_4071 msgid "Droits de douanes à l'importation" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1109 #: model:account.account.template,name:l10n_ch.ch_coa_1109 msgid "Ducroire" msgstr "Delcredere" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1100 #: model:account.account.template,name:l10n_ch.ch_coa_1100 msgid "Débiteurs" msgstr "Crediti da forniture e prestazioni (debitori)" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_2201 #: model:account.account.template,name:l10n_ch.ch_coa_2201 msgid "Décompte TVA" msgstr "IVA, rendiconto" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_4009 #: model:account.account.template,name:l10n_ch.ch_coa_4009 msgid "Déductions obtenues sur achats" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_3009 #: model:account.account.template,name:l10n_ch.ch_coa_3009 msgid "Déductions sur ventes" msgstr "Diminuzione di ricavi" #. module: l10n_ch -#: model:ir.model.fields,field_description:l10n_ch.field_res_bank_l10n_ch_postal_eur -msgid "EUR ISR reference" -msgstr "" - -#. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_4540 #: model:account.account.template,name:l10n_ch.ch_coa_4540 msgid "Eau" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_4500 #: model:account.account.template,name:l10n_ch.ch_coa_4500 msgid "Electricité" msgstr "" @@ -681,323 +539,202 @@ msgid "Email Templates" msgstr "" #. module: l10n_ch -#: model:ir.model,name:l10n_ch.model_mail_compose_message -msgid "Email composition wizard" -msgstr "" - -#. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_2450 #: model:account.account.template,name:l10n_ch.ch_coa_2450 msgid "Emprunts" msgstr "Prestiti" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_2430 #: model:account.account.template,name:l10n_ch.ch_coa_2430 msgid "Emprunts obligataires" msgstr "Prestiti obbligazionari" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_2120 -#: model:account.account,name:l10n_ch.1_ch_coa_2420 #: model:account.account.template,name:l10n_ch.ch_coa_2120 #: model:account.account.template,name:l10n_ch.ch_coa_2420 msgid "Engagements de financement par leasing" msgstr "Impegni leasing finanziari" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_6100 #: model:account.account.template,name:l10n_ch.ch_coa_6100 msgid "" "Entretien, réparations et remplacement des inst. servant à l’exploitation" msgstr "Manutenzioni, riparazioni e sostituzione immobilizzazioni mobiliari" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1570 #: model:account.account.template,name:l10n_ch.ch_coa_1570 msgid "Equipements et Installations" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_3800 -#: model:account.account,name:l10n_ch.1_ch_coa_4900 #: model:account.account.template,name:l10n_ch.ch_coa_3800 #: model:account.account.template,name:l10n_ch.ch_coa_4900 msgid "Escomptes" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_4530 #: model:account.account.template,name:l10n_ch.ch_coa_4530 msgid "Essence" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_3804 #: model:account.account.template,name:l10n_ch.ch_coa_3804 msgid "Frais d'encaissement" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_3807 #: model:account.account.template,name:l10n_ch.ch_coa_3807 msgid "Frais d'expédition" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_4072 #: model:account.account.template,name:l10n_ch.ch_coa_4072 msgid "Frais de transport à l'achat" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_4070 #: model:account.account.template,name:l10n_ch.ch_coa_4070 msgid "Frêts à l'achat" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_4510 #: model:account.account.template,name:l10n_ch.ch_coa_4510 msgid "Gaz" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1770 #: model:account.account.template,name:l10n_ch.ch_coa_1770 msgid "Goodwill" msgstr "Goodwill" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1441 -#: model:account.account,name:l10n_ch.1_ch_coa_2451 #: model:account.account.template,name:l10n_ch.ch_coa_1441 #: model:account.account.template,name:l10n_ch.ch_coa_2451 msgid "Hypothèques" msgstr "Prestiti ipotecari" #. module: l10n_ch -#: model:ir.actions.report.xml,name:l10n_ch.l10n_ch_isr_report -msgid "ISR" -msgstr "" - -#. module: l10n_ch -#: model_terms:ir.ui.view,arch_db:l10n_ch.l10n_ch_isr_report_template -msgid "ISR for invoice" -msgstr "" - -#. module: l10n_ch -#: model:ir.model.fields,help:l10n_ch.field_account_invoice_l10n_ch_isr_number_spaced -msgid "" -"ISR number split in blocks of 5 characters (right-justified), to generate " -"ISR report." -msgstr "" - -#. module: l10n_ch -#: model_terms:ir.ui.view,arch_db:l10n_ch.isr_invoice_search_view -msgid "ISR reference number" -msgstr "" - -#. module: l10n_ch -#: code:addons/l10n_ch/models/mail_compose_message.py:23 -#, python-format -msgid "ISR sent" -msgstr "" - -#. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1600 #: model:account.account.template,name:l10n_ch.ch_coa_1600 msgid "Immeubles d’exploitation" msgstr "Immobili aziendali" #. module: l10n_ch -#: model:account.fiscal.position,name:l10n_ch.1_fiscal_position_template_import #: model:account.fiscal.position.template,name:l10n_ch.fiscal_position_template_import msgid "Import/Export" msgstr "Import/Export" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1176 #: model:account.account.template,name:l10n_ch.ch_coa_1176 msgid "Impôt anticipé" msgstr "Imposta preventiva" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_2206 #: model:account.account.template,name:l10n_ch.ch_coa_2206 msgid "Impôt anticipé dû" msgstr "Imposta preventiva" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1171 #: model:account.account.template,name:l10n_ch.ch_coa_1171 msgid "Impôt préalable: TVA s/investissements et autres charges d’exploitation" msgstr "IVA, imposta precedente su investimenti e altri costi d’esercizio" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1170 #: model:account.account.template,name:l10n_ch.ch_coa_1170 msgid "Impôt préalable: TVA s/matériel, marchandises, prestations et énergie" msgstr "IVA, Imposta precedente su materiale, merce, servizi e energia" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1189 -#: model:account.account,name:l10n_ch.1_ch_coa_2279 #: model:account.account.template,name:l10n_ch.ch_coa_1189 #: model:account.account.template,name:l10n_ch.ch_coa_2279 msgid "Impôt à la source" msgstr "Imposte alla fonte" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_2208 -#: model:account.account,name:l10n_ch.1_ch_coa_8900 #: model:account.account.template,name:l10n_ch.ch_coa_2208 #: model:account.account.template,name:l10n_ch.ch_coa_8900 msgid "Impôts directs" msgstr "Imposte dirette" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1550 #: model:account.account.template,name:l10n_ch.ch_coa_1550 msgid "Installations de stockage" msgstr "" #. module: l10n_ch -#: model:ir.model,name:l10n_ch.model_account_invoice -msgid "Invoice" -msgstr "" - -#. module: l10n_ch -#: model:ir.model.fields,field_description:l10n_ch.field_account_invoice_l10n_ch_isr_number -msgid "L10N Ch Isr Number" -msgstr "" - -#. module: l10n_ch -#: model:ir.model.fields,field_description:l10n_ch.field_account_invoice_l10n_ch_isr_number_spaced -msgid "L10N Ch Isr Number Spaced" -msgstr "" - -#. module: l10n_ch -#: model:ir.model.fields,field_description:l10n_ch.field_account_invoice_l10n_ch_isr_optical_line -msgid "L10N Ch Isr Optical Line" -msgstr "" - -#. module: l10n_ch -#: model:ir.model.fields,field_description:l10n_ch.field_account_invoice_l10n_ch_isr_postal -msgid "L10N Ch Isr Postal" -msgstr "" - -#. module: l10n_ch -#: model:ir.model.fields,field_description:l10n_ch.field_account_invoice_l10n_ch_isr_postal_formatted -msgid "L10N Ch Isr Postal Formatted" -msgstr "" - -#. module: l10n_ch -#: model:ir.model.fields,field_description:l10n_ch.field_account_invoice_l10n_ch_isr_sent -msgid "L10N Ch Isr Sent" -msgstr "" - -#. module: l10n_ch -#: model:ir.model.fields,field_description:l10n_ch.field_account_invoice_l10n_ch_isr_valid -msgid "L10N Ch Isr Valid" -msgstr "" - -#. module: l10n_ch -#: model:ir.model.fields,field_description:l10n_ch.field_res_partner_bank_l10n_ch_postal -msgid "L10N Ch Postal" -msgstr "" - -#. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_6260 #: model:account.account.template,name:l10n_ch.ch_coa_6260 msgid "Leasing et location de véhicules" msgstr "Leasing e noleggio auto" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_6105 #: model:account.account.template,name:l10n_ch.ch_coa_6105 msgid "Leasing immobilisations corporelles meubles" msgstr "Costi leasing immobilizzazioni mobiliari" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1520 +#: model:account.account.template,name:l10n_ch.l10nch_chart_template_liquidity_transfer +msgid "Liquidity Transfer" +msgstr "" + +#. module: l10n_ch #: model:account.account.template,name:l10n_ch.ch_coa_1520 msgid "Machines de bureau, informatique, systèmes de communication" msgstr "Macchine ufficio, informatica e tecnologia della comunicazione" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1500 #: model:account.account.template,name:l10n_ch.ch_coa_1500 msgid "Machines et appareils" msgstr "Macchine e attrezzature" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1200 #: model:account.account.template,name:l10n_ch.ch_coa_1200 msgid "Marchandises commerciales" msgstr "Merce di rivendita" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1250 #: model:account.account.template,name:l10n_ch.ch_coa_1250 msgid "Marchandises en consignation" msgstr "Merce in consegna" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1220 #: model:account.account.template,name:l10n_ch.ch_coa_1220 msgid "Matières auxiliaires" msgstr "Materia ausiliaria" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1230 #: model:account.account.template,name:l10n_ch.ch_coa_1230 msgid "Matières consommables" msgstr "Materiale di consumo" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1210 #: model:account.account.template,name:l10n_ch.ch_coa_1210 msgid "Matières premières" msgstr "Materia prima" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_4520 #: model:account.account.template,name:l10n_ch.ch_coa_4520 msgid "Mazout" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1510 #: model:account.account.template,name:l10n_ch.ch_coa_1510 msgid "Mobilier et installations" msgstr "Mobilio e installazioni" #. module: l10n_ch -#: model:ir.model.fields,help:l10n_ch.field_account_invoice_l10n_ch_isr_optical_line -msgid "Optical reading line, as it will be printed on ISR" -msgstr "" - -#. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1540 #: model:account.account.template,name:l10n_ch.ch_coa_1540 msgid "Outillages et appareils" msgstr "Utensili e apparecchiature" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1480 #: model:account.account.template,name:l10n_ch.ch_coa_1480 msgid "Participations" msgstr "Partecipazioni" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_4086 #: model:account.account.template,name:l10n_ch.ch_coa_4086 msgid "Pertes de matières" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_3805 #: model:account.account.template,name:l10n_ch.ch_coa_3805 msgid "Pertes sur créances clients, variation ducroire" msgstr "" @@ -1008,92 +745,51 @@ msgid "Plan comptable 2015" msgstr "Piano Conti 2015" #. module: l10n_ch -#: model:ir.model.fields,help:l10n_ch.field_account_invoice_l10n_ch_isr_postal_formatted -msgid "" -"Postal reference of the bank, formated with '-' and without the padding " -"zeros, to generate ISR report." -msgstr "" - -#. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_4400 #: model:account.account.template,name:l10n_ch.ch_coa_4400 msgid "Prestations / travaux de tiers" msgstr "Lavori di terzi / prestazioni di subappaltanti" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_3700 #: model:account.account.template,name:l10n_ch.ch_coa_3700 msgid "Prestations propres" msgstr "Lavori interni" #. module: l10n_ch -#: model_terms:ir.ui.view,arch_db:l10n_ch.isr_invoice_form -msgid "Print ISR" -msgstr "" - -#. module: l10n_ch -#: model:ir.model.fields,field_description:l10n_ch.field_res_company_l10n_ch_isr_print_bank_location -msgid "Print bank location" -msgstr "" - -#. module: l10n_ch -#: model:ir.model.fields,field_description:l10n_ch.field_account_config_settings_l10n_ch_isr_print_bank_location -msgid "Print bank on ISR" -msgstr "" - -#. module: l10n_ch -#: model_terms:ir.ui.view,arch_db:l10n_ch.l10n_ch-isr_account_config_settings -msgid "" -"Print the coordinates of your bank under the 'Payment for' title of the " -"ISR.\n" -" Your address will be moved to the 'in favour " -"of' section." -msgstr "" - -#. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_7000 #: model:account.account.template,name:l10n_ch.ch_coa_7000 msgid "Produits accessoires" msgstr "Ricavi attività accessoria" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_7500 #: model:account.account.template,name:l10n_ch.ch_coa_7500 msgid "Produits des immeubles d‘exploitation" msgstr "Ricavi immobili aziendali" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_2301 #: model:account.account.template,name:l10n_ch.ch_coa_2301 msgid "Produits encaissés d’avance" msgstr "Ricavi incassati dell’anno seguente" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_8510 #: model:account.account.template,name:l10n_ch.ch_coa_8510 msgid "Produits extraordinaires, exceptionnels ou hors période" msgstr "Ricavi straordinari, unici o relativi ad altri periodi contabili" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_6950 #: model:account.account.template,name:l10n_ch.ch_coa_6950 msgid "Produits financiers" msgstr "Ricavi finanziari" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_8100 #: model:account.account.template,name:l10n_ch.ch_coa_8100 msgid "Produits hors exploitation" msgstr "Ricavi estranei" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1301 #: model:account.account.template,name:l10n_ch.ch_coa_1301 msgid "Produits à recevoir" msgstr "Ricavi da incassare" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_2980 #: model:account.account.template,name:l10n_ch.ch_coa_2980 msgid "" "Propres actions, parts sociales, droits de participations (poste négatif)" @@ -1101,96 +797,76 @@ msgstr "" "Azioni proprie, parti sociali, diritti di partecipazione (posta negativa)" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_2600 #: model:account.account.template,name:l10n_ch.ch_coa_2600 msgid "Provisions" msgstr "Accantonamenti" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_2330 #: model:account.account.template,name:l10n_ch.ch_coa_2330 msgid "Provisions à court terme" msgstr "Accantonamenti a breve termine" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1440 #: model:account.account.template,name:l10n_ch.ch_coa_1440 msgid "Prêts" msgstr "Prestiti" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_6600 #: model:account.account.template,name:l10n_ch.ch_coa_6600 msgid "Publicité" msgstr "Costi pubblicitari" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_3801 #: model:account.account.template,name:l10n_ch.ch_coa_3801 msgid "Rabais et réduction de prix" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_4901 #: model:account.account.template,name:l10n_ch.ch_coa_4901 msgid "Rabais et réductions de prix" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_3802 -#: model:account.account,name:l10n_ch.1_ch_coa_4092 #: model:account.account.template,name:l10n_ch.ch_coa_3802 #: model:account.account.template,name:l10n_ch.ch_coa_4092 msgid "Ristournes" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_2940 #: model:account.account.template,name:l10n_ch.ch_coa_2940 msgid "Réserves d‘évaluation" msgstr "Riserve da rivalutazioni" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_2960 #: model:account.account.template,name:l10n_ch.ch_coa_2960 msgid "Réserves libres" msgstr "Riserve facoltative da utili" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_2950 #: model:account.account.template,name:l10n_ch.ch_coa_2950 msgid "Réserves légales issues du bénéfice" msgstr "Riserva legale da utili" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_2900 #: model:account.account.template,name:l10n_ch.ch_coa_2900 msgid "Réserves légales issues du capital" msgstr "Riserva legale da capitale" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_5000 #: model:account.account.template,name:l10n_ch.ch_coa_5000 msgid "Salaires" msgstr "Salari" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1260 #: model:account.account.template,name:l10n_ch.ch_coa_1260 msgid "Stocks de produits finis" msgstr "Prodotti finiti" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1270 #: model:account.account.template,name:l10n_ch.ch_coa_1270 msgid "Stocks de produits semi-ouvrés" msgstr "" -#. module: l10n_ch -#: model:ir.ui.menu,name:l10n_ch.account_reports_ch_statements_menu -msgid "Swiss Statements" -msgstr "" - #. module: l10n_ch #: model:account.account.tag,name:l10n_ch.vat_tag_dedouanement msgid "Switzerland VAT Form: 100% dédouanement (base in grid 400)" @@ -1232,13 +908,13 @@ msgid "Switzerland VAT Form: grid 280" msgstr "" #. module: l10n_ch -#: model:account.account.tag,name:l10n_ch.vat_tag_301_a -msgid "Switzerland VAT Form: grid 301 base" +#: model:account.account.tag,name:l10n_ch.vat_tag_302_a +msgid "Switzerland VAT Form: grid 302 base" msgstr "" #. module: l10n_ch -#: model:account.account.tag,name:l10n_ch.vat_tag_301_b -msgid "Switzerland VAT Form: grid 301 tax" +#: model:account.account.tag,name:l10n_ch.vat_tag_302_b +msgid "Switzerland VAT Form: grid 302 tax" msgstr "" #. module: l10n_ch @@ -1252,13 +928,13 @@ msgid "Switzerland VAT Form: grid 311 tax" msgstr "" #. module: l10n_ch -#: model:account.account.tag,name:l10n_ch.vat_tag_341_a -msgid "Switzerland VAT Form: grid 341 base" +#: model:account.account.tag,name:l10n_ch.vat_tag_342_a +msgid "Switzerland VAT Form: grid 342 base" msgstr "" #. module: l10n_ch -#: model:account.account.tag,name:l10n_ch.vat_tag_341_b -msgid "Switzerland VAT Form: grid 341 tax" +#: model:account.account.tag,name:l10n_ch.vat_tag_342_b +msgid "Switzerland VAT Form: grid 342 tax" msgstr "" #. module: l10n_ch @@ -1271,6 +947,16 @@ msgstr "" msgid "Switzerland VAT Form: grid 381 tax" msgstr "" +#. module: l10n_ch +#: model:account.account.tag,name:l10n_ch.vat_tag_382_a +msgid "Switzerland VAT Form: grid 382 base" +msgstr "" + +#. module: l10n_ch +#: model:account.account.tag,name:l10n_ch.vat_tag_382_b +msgid "Switzerland VAT Form: grid 382 tax" +msgstr "" + #. module: l10n_ch #: model:account.account.tag,name:l10n_ch.vat_tag_400 msgid "Switzerland VAT Form: grid 400" @@ -1297,304 +983,228 @@ msgid "Switzerland VAT Form: grid 420" msgstr "" #. module: l10n_ch -#: model:account.tax,name:l10n_ch.1_vat_O_import +#: model:account.tax.group,name:l10n_ch.tax_group_tva_0 +msgid "TVA 0%" +msgstr "" + +#. module: l10n_ch #: model:account.tax.template,name:l10n_ch.vat_O_import msgid "TVA 0% Importations de biens et services" msgstr "IVA 0% Importazioni di bene e servizi" #. module: l10n_ch -#: model:account.tax,name:l10n_ch.1_vat_O_exclude #: model:account.tax.template,name:l10n_ch.vat_O_exclude msgid "TVA 0% exclue" msgstr "IVA 0% Esclusa" #. module: l10n_ch -#: model:account.tax,name:l10n_ch.1_vat_25_purchase_incl +#: model:account.tax.group,name:l10n_ch.tax_group_tva_25 +msgid "TVA 2.5%" +msgstr "" + +#. module: l10n_ch #: model:account.tax.template,name:l10n_ch.vat_25_purchase_incl msgid "TVA 2.5% sur achat B&S (Incl. TR)" msgstr "" #. module: l10n_ch -#: model:account.tax,name:l10n_ch.1_vat_25_purchase #: model:account.tax.template,name:l10n_ch.vat_25_purchase msgid "TVA 2.5% sur achat B&S (TR)" msgstr "TVA 2.5% sur achat B&S (TR)" #. module: l10n_ch -#: model:account.tax,name:l10n_ch.1_vat_25_invest_incl #: model:account.tax.template,name:l10n_ch.vat_25_invest_incl msgid "TVA 2.5% sur invest. et autres ch. (Incl. TR)" msgstr "" #. module: l10n_ch -#: model:account.tax,name:l10n_ch.1_vat_25_invest #: model:account.tax.template,name:l10n_ch.vat_25_invest msgid "TVA 2.5% sur invest. et autres ch. (TR)" msgstr "IVA 2.5% Investimenti e altri costi (TR)" #. module: l10n_ch -#: model:account.tax,name:l10n_ch.1_vat_38_purchase_incl -#: model:account.tax.template,name:l10n_ch.vat_38_purchase_incl -msgid "TVA 3.8% sur achat B&S (Incl. TS)" +#: model:account.tax.group,name:l10n_ch.tax_group_tva_37 +msgid "TVA 3.7%" msgstr "" #. module: l10n_ch -#: model:account.tax,name:l10n_ch.1_vat_38_purchase -#: model:account.tax.template,name:l10n_ch.vat_38_purchase -msgid "TVA 3.8% sur achat B&S (TS)" -msgstr "TVA 3.8% sur achat B&S (TS)" - -#. module: l10n_ch -#: model:account.tax,name:l10n_ch.1_vat_38_invest_incl -#: model:account.tax.template,name:l10n_ch.vat_38_invest_incl -msgid "TVA 3.8% sur invest. et autres ch. (Incl. TS)" +#: model:account.tax.template,name:l10n_ch.vat_37_purchase_incl +msgid "TVA 3.7% sur achat B&S (Incl. TS)" msgstr "" #. module: l10n_ch -#: model:account.tax,name:l10n_ch.1_vat_38_invest -#: model:account.tax.template,name:l10n_ch.vat_38_invest -msgid "TVA 3.8% sur invest. et autres ch. (TS)" -msgstr "IVA 3.8% Investimenti e altri costi (TS)" - -#. module: l10n_ch -#: model:account.tax,name:l10n_ch.1_vat_80_purchase_incl -#: model:account.tax.template,name:l10n_ch.vat_80_purchase_incl -msgid "TVA 8.0% sur achat B&S (Incl. TN)" +#: model:account.tax.template,name:l10n_ch.vat_37_purchase +msgid "TVA 3.7% sur achat B&S (TS)" msgstr "" #. module: l10n_ch -#: model:account.tax,name:l10n_ch.1_vat_80_purchase -#: model:account.tax.template,name:l10n_ch.vat_80_purchase -msgid "TVA 8.0% sur achat B&S (TN)" -msgstr "TVA 8.0% sur achat B&S (TN)" - -#. module: l10n_ch -#: model:account.tax,name:l10n_ch.1_vat_80_invest_incl -#: model:account.tax.template,name:l10n_ch.vat_80_invest_incl -msgid "TVA 8.0% sur invest. et autres ch. (Incl. TN)" +#: model:account.tax.template,name:l10n_ch.vat_37_invest_incl +msgid "TVA 3.7% sur invest. et autres ch. (Incl. TS)" msgstr "" #. module: l10n_ch -#: model:account.tax,name:l10n_ch.1_vat_80_invest -#: model:account.tax.template,name:l10n_ch.vat_80_invest -msgid "TVA 8.0% sur invest. et autres ch. (TN)" -msgstr "IVA 8.0% Investimenti e altri costi (TN)" +#: model:account.tax.template,name:l10n_ch.vat_37_invest +msgid "TVA 3.7% sur invest. et autres ch. (TS)" +msgstr "" + +#. module: l10n_ch +#: model:account.tax.group,name:l10n_ch.tax_group_tva_77 +msgid "TVA 7.7%" +msgstr "" + +#. module: l10n_ch +#: model:account.tax.template,name:l10n_ch.vat_77_purchase_incl +msgid "TVA 7.7% sur achat B&S (Incl. TN)" +msgstr "" + +#. module: l10n_ch +#: model:account.tax.template,name:l10n_ch.vat_77_purchase +msgid "TVA 7.7% sur achat B&S (TN)" +msgstr "" + +#. module: l10n_ch +#: model:account.tax.template,name:l10n_ch.vat_77_purchase_reverse +msgid "TVA 7.7% sur achat service a l'etranger (reverse charge)" +msgstr "" + +#. module: l10n_ch +#: model:account.tax.template,name:l10n_ch.vat_77_invest_incl +msgid "TVA 7.7% sur invest. et autres ch. (Incl. TN)" +msgstr "" + +#. module: l10n_ch +#: model:account.tax.template,name:l10n_ch.vat_77_invest +msgid "TVA 7.7% sur invest. et autres ch. (TN)" +msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_2200 #: model:account.account.template,name:l10n_ch.ch_coa_2200 msgid "TVA due" msgstr "IVA dovuta" #. module: l10n_ch -#: model:account.tax,name:l10n_ch.1_vat_XO #: model:account.tax.template,name:l10n_ch.vat_XO msgid "TVA due a 0% (Exportations)" msgstr "IVA Vendite 0% (Export)" #. module: l10n_ch -#: model:account.tax,name:l10n_ch.1_vat_25 #: model:account.tax.template,name:l10n_ch.vat_25 msgid "TVA due a 2.5% (TR)" msgstr "IVA Vendite al 2.5% (TR)" #. module: l10n_ch -#: model:account.tax,name:l10n_ch.1_vat_38 -#: model:account.tax.template,name:l10n_ch.vat_38 -msgid "TVA due a 3.8% (TS)" -msgstr "IVA Vendite al 3.8% (TS)" +#: model:account.tax.template,name:l10n_ch.vat_37 +msgid "TVA due a 3.7% (TS)" +msgstr "" #. module: l10n_ch -#: model:account.tax,name:l10n_ch.1_vat_80 -#: model:account.tax.template,name:l10n_ch.vat_80 -msgid "TVA due a 8.0% (TN)" -msgstr "IVA Vendite al 8.0% (TN)" +#: model:account.tax.template,name:l10n_ch.vat_77 +msgid "TVA due a 7.7% (TN)" +msgstr "" + +#. module: l10n_ch +#: model:account.tax.template,name:l10n_ch.vat_77_purchase_return +msgid "TVA due a 7.7% (TN) (return)" +msgstr "" #. module: l10n_ch -#: model:account.tax,name:l10n_ch.1_vat_25_incl #: model:account.tax.template,name:l10n_ch.vat_25_incl msgid "TVA due à 2.5% (Incl. TR)" msgstr "" #. module: l10n_ch -#: model:account.tax,name:l10n_ch.1_vat_38_incl -#: model:account.tax.template,name:l10n_ch.vat_38_incl -msgid "TVA due à 3.8% (Incl. TS)" +#: model:account.tax.template,name:l10n_ch.vat_37_incl +msgid "TVA due à 3.7% (Incl. TS)" msgstr "" #. module: l10n_ch -#: model:account.tax,name:l10n_ch.1_vat_80_incl -#: model:account.tax.template,name:l10n_ch.vat_80_incl -msgid "TVA due à 8.0% (Incl. TN)" +#: model:account.tax.template,name:l10n_ch.vat_77_incl +msgid "TVA due à 7.7% (Incl. TN)" msgstr "" #. module: l10n_ch -#: model:ir.model.fields,help:l10n_ch.field_res_partner_bank_l10n_ch_postal -msgid "The ISR number of the company within the bank" -msgstr "" - -#. module: l10n_ch -#: model:ir.model.fields,help:l10n_ch.field_account_invoice_l10n_ch_currency_name -msgid "The name of this invoice's currency" -msgstr "" - -#. module: l10n_ch -#: model:ir.model.fields,help:l10n_ch.field_account_invoice_l10n_ch_isr_postal -msgid "The postal reference identifying the bank managing this ISR." -msgstr "" - -#. module: l10n_ch -#: model:ir.model.fields,help:l10n_ch.field_res_bank_l10n_ch_postal_chf -msgid "" -"The postal reference of the bank, used to generate ISR payment slips in CHF." -msgstr "" - -#. module: l10n_ch -#: model:ir.model.fields,help:l10n_ch.field_res_bank_l10n_ch_postal_eur -msgid "" -"The postal reference of the bank, used to generate ISR payment slips in EUR." -msgstr "" - -#. module: l10n_ch -#: model:ir.model.fields,help:l10n_ch.field_account_invoice_l10n_ch_isr_number -msgid "The reference number associated with this invoice" -msgstr "" - -#. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1060 #: model:account.account.template,name:l10n_ch.ch_coa_1060 msgid "Titres" msgstr "Titoli" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1400 #: model:account.account.template,name:l10n_ch.ch_coa_1400 msgid "Titres à long terme" msgstr "Titoli" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_transfer_account_id -#: model:account.account.template,name:l10n_ch.transfer_account_id -msgid "Transferts internes" -msgstr "" - -#. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1280 #: model:account.account.template,name:l10n_ch.ch_coa_1280 msgid "Travaux en cours" msgstr "Prodotti in corso di fabbricazione" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_3940 #: model:account.account.template,name:l10n_ch.ch_coa_3940 msgid "Variation de la valeur des prestations non facturées" msgstr "Variazione prestazioni di servizi non fatturate" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1287 #: model:account.account.template,name:l10n_ch.ch_coa_1287 msgid "Variation de la valeur des travaux en cours" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1277 #: model:account.account.template,name:l10n_ch.ch_coa_1277 msgid "Variation de stock produits semi-ouvrés" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1267 #: model:account.account.template,name:l10n_ch.ch_coa_1267 msgid "Variation de stocks de produits finis" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1207 -#: model:account.account,name:l10n_ch.1_ch_coa_4800 #: model:account.account.template,name:l10n_ch.ch_coa_1207 #: model:account.account.template,name:l10n_ch.ch_coa_4800 msgid "Variation des stocks de marchandises" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_4801 #: model:account.account.template,name:l10n_ch.ch_coa_4801 msgid "Variation des stocks de matières premières" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_3901 #: model:account.account.template,name:l10n_ch.ch_coa_3901 msgid "Variation des stocks de produits finis" msgstr "Variazione delle scorte di prodotti finiti" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_3900 #: model:account.account.template,name:l10n_ch.ch_coa_3900 msgid "Variation des stocks de produits semi-finis" msgstr "Variazione delle scorte di prodotti in corso di fabbricazione" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1217 #: model:account.account.template,name:l10n_ch.ch_coa_1217 msgid "Variation des stocks des matières premières" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_4008 -#: model:account.account,name:l10n_ch.1_ch_coa_4080 #: model:account.account.template,name:l10n_ch.ch_coa_4008 #: model:account.account.template,name:l10n_ch.ch_coa_4080 msgid "Variations de stocks" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_3200 #: model:account.account.template,name:l10n_ch.ch_coa_3200 msgid "Ventes de marchandises" msgstr "Ricavi merci di rivendita" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_3400 #: model:account.account.template,name:l10n_ch.ch_coa_3400 msgid "Ventes de prestations" msgstr "Ricavi prestazioni di servizi" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_3000 #: model:account.account.template,name:l10n_ch.ch_coa_3000 msgid "Ventes de produits fabriqués" msgstr "Ricavi prodotti fabbricati" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1530 #: model:account.account.template,name:l10n_ch.ch_coa_1530 msgid "Véhicules" msgstr "Veicoli" - -#. module: l10n_ch -#: code:addons/l10n_ch/models/account_invoice.py:153 -#, python-format -msgid "" -"You cannot generate an ISR yet.\n" -"\n" -" For this, you need to :\n" -"\n" -" - set a valid postal account number (or " -"an IBAN referencing one) for your company\n" -"\n" -" - define its bank\n" -"\n" -" - associate this bank with a postal " -"reference for the currency used in this invoice\n" -"\n" -" - fill the 'bank account' field of the " -"invoice with the postal to be used to receive the related payment. A default " -"account will be automatically set for all invoices created after you defined " -"a postal account for your company." -msgstr "" - -#. module: l10n_ch -#: model:ir.model,name:l10n_ch.model_account_config_settings -msgid "account.config.settings" -msgstr "" diff --git a/addons/l10n_ch/i18n_extra/l10n_ch.pot b/addons/l10n_ch/i18n_extra/l10n_ch.pot index e2c03bcba8b..73b91c2b356 100644 --- a/addons/l10n_ch/i18n_extra/l10n_ch.pot +++ b/addons/l10n_ch/i18n_extra/l10n_ch.pot @@ -4,10 +4,10 @@ # msgid "" msgstr "" -"Project-Id-Version: Odoo Server 10.saas~15+e\n" +"Project-Id-Version: Odoo Server 12.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-03-14 15:52+0000\n" -"PO-Revision-Date: 2017-03-14 15:52+0000\n" +"POT-Creation-Date: 2019-02-12 05:39+0000\n" +"PO-Revision-Date: 2019-02-12 05:39+0000\n" "Last-Translator: <>\n" "Language-Team: \n" "MIME-Version: 1.0\n" @@ -16,496 +16,393 @@ msgstr "" "Plural-Forms: \n" #. module: l10n_ch -#: model:account.tax,description:l10n_ch.1_vat_O_exclude #: model:account.tax.template,description:l10n_ch.vat_O_exclude msgid "0% excl." msgstr "" #. module: l10n_ch -#: model:account.tax,description:l10n_ch.1_vat_O_import #: model:account.tax.template,description:l10n_ch.vat_O_import msgid "0% import." msgstr "" #. module: l10n_ch -#: model:account.tax,name:l10n_ch.1_vat_100_import #: model:account.tax.template,name:l10n_ch.vat_100_import msgid "100% dédouanement TVA" msgstr "" #. module: l10n_ch -#: model:account.tax,description:l10n_ch.1_vat_100_import #: model:account.tax.template,description:l10n_ch.vat_100_import msgid "100% imp." msgstr "" #. module: l10n_ch -#: model:account.tax,description:l10n_ch.1_vat_25 #: model:account.tax.template,description:l10n_ch.vat_25 msgid "2.5%" msgstr "" #. module: l10n_ch -#: model:account.tax,description:l10n_ch.1_vat_25_incl #: model:account.tax.template,description:l10n_ch.vat_25_incl msgid "2.5% Incl." msgstr "" #. module: l10n_ch -#: model:account.tax,description:l10n_ch.1_vat_25_purchase #: model:account.tax.template,description:l10n_ch.vat_25_purchase msgid "2.5% achat" msgstr "" #. module: l10n_ch -#: model:account.tax,description:l10n_ch.1_vat_25_purchase_incl #: model:account.tax.template,description:l10n_ch.vat_25_purchase_incl msgid "2.5% achat Incl." msgstr "" #. module: l10n_ch -#: model:account.tax,description:l10n_ch.1_vat_25_invest #: model:account.tax.template,description:l10n_ch.vat_25_invest msgid "2.5% invest." msgstr "" #. module: l10n_ch -#: model:account.tax,description:l10n_ch.1_vat_25_invest_incl #: model:account.tax.template,description:l10n_ch.vat_25_invest_incl msgid "2.5% invest. Incl." msgstr "" #. module: l10n_ch -#: model:account.tax,description:l10n_ch.1_vat_38 -#: model:account.tax.template,description:l10n_ch.vat_38 -msgid "3.8%" +#: model:account.tax.template,description:l10n_ch.vat_37 +msgid "3.7%" msgstr "" #. module: l10n_ch -#: model:account.tax,description:l10n_ch.1_vat_38_incl -#: model:account.tax.template,description:l10n_ch.vat_38_incl -msgid "3.8% Incl." +#: model:account.tax.template,description:l10n_ch.vat_37_incl +msgid "3.7% Incl." msgstr "" #. module: l10n_ch -#: model:account.tax,description:l10n_ch.1_vat_38_purchase -#: model:account.tax.template,description:l10n_ch.vat_38_purchase -msgid "3.8% achat" +#: model:account.tax.template,description:l10n_ch.vat_37_purchase +msgid "3.7% achat" msgstr "" #. module: l10n_ch -#: model:account.tax,description:l10n_ch.1_vat_38_purchase_incl -#: model:account.tax.template,description:l10n_ch.vat_38_purchase_incl -msgid "3.8% achat Incl." +#: model:account.tax.template,description:l10n_ch.vat_37_purchase_incl +msgid "3.7% achat Incl." msgstr "" #. module: l10n_ch -#: model:account.tax,description:l10n_ch.1_vat_38_invest -#: model:account.tax.template,description:l10n_ch.vat_38_invest -msgid "3.8% invest" +#: model:account.tax.template,description:l10n_ch.vat_37_invest +msgid "3.7% invest" msgstr "" #. module: l10n_ch -#: model:account.tax,description:l10n_ch.1_vat_38_invest_incl -#: model:account.tax.template,description:l10n_ch.vat_38_invest_incl -msgid "3.8% invest Incl." +#: model:account.tax.template,description:l10n_ch.vat_37_invest_incl +msgid "3.7% invest Incl." msgstr "" #. module: l10n_ch -#: model:account.tax,description:l10n_ch.1_vat_80 -#: model:account.tax.template,description:l10n_ch.vat_80 -msgid "8.0%" +#: model:account.tax.template,description:l10n_ch.vat_77 +msgid "7.7%" msgstr "" #. module: l10n_ch -#: model:account.tax,description:l10n_ch.1_vat_80_incl -#: model:account.tax.template,description:l10n_ch.vat_80_incl -msgid "8.0% Incl." +#: model:account.tax.template,description:l10n_ch.vat_77_incl +msgid "7.7% Incl." msgstr "" #. module: l10n_ch -#: model:account.tax,description:l10n_ch.1_vat_80_purchase -#: model:account.tax.template,description:l10n_ch.vat_80_purchase -msgid "8.0% achat" +#: model:account.tax.template,description:l10n_ch.vat_77_purchase +#: model:account.tax.template,description:l10n_ch.vat_77_purchase_reverse +msgid "7.7% achat" msgstr "" #. module: l10n_ch -#: model:account.tax,description:l10n_ch.1_vat_80_purchase_incl -#: model:account.tax.template,description:l10n_ch.vat_80_purchase_incl -msgid "8.0% achat Incl." +#: model:account.tax.template,description:l10n_ch.vat_77_purchase_return +msgid "7.7% achat (return)" msgstr "" #. module: l10n_ch -#: model:account.tax,description:l10n_ch.1_vat_80_invest -#: model:account.tax.template,description:l10n_ch.vat_80_invest -msgid "8.0% invest." +#: model:account.tax.template,description:l10n_ch.vat_77_purchase_incl +msgid "7.7% achat Incl." msgstr "" #. module: l10n_ch -#: model:account.tax,description:l10n_ch.1_vat_80_invest_incl -#: model:account.tax.template,description:l10n_ch.vat_80_invest_incl -msgid "8.0% invest. Incl." +#: model:account.tax.template,description:l10n_ch.vat_77_invest +msgid "7.7% invest." +msgstr "" + +#. module: l10n_ch +#: model:account.tax.template,description:l10n_ch.vat_77_invest_incl +msgid "7.7% invest. Incl." msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_4200 #: model:account.account.template,name:l10n_ch.ch_coa_4200 msgid "Achats de marchandises destinées à la revente" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_2030 #: model:account.account.template,name:l10n_ch.ch_coa_2030 msgid "Acomptes de clients" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1208 #: model:account.account.template,name:l10n_ch.ch_coa_1208 msgid "Acomptes sur les marchandises commerciales" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1218 #: model:account.account.template,name:l10n_ch.ch_coa_1218 msgid "Acomptes sur matières premières" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1149 #: model:account.account.template,name:l10n_ch.ch_coa_1149 msgid "Ajustement de la valeur des avances et des prêts" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1199 #: model:account.account.template,name:l10n_ch.ch_coa_1199 msgid "Ajustement de la valeur des créances à court terme" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1449 #: model:account.account.template,name:l10n_ch.ch_coa_1449 msgid "Ajustement de la valeur des créances à long terme" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1779 #: model:account.account.template,name:l10n_ch.ch_coa_1779 msgid "Ajustement de la valeur des goodwill" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1489 #: model:account.account.template,name:l10n_ch.ch_coa_1489 msgid "Ajustement de la valeur des participations" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1069 -#: model:account.account,name:l10n_ch.1_ch_coa_1409 #: model:account.account.template,name:l10n_ch.ch_coa_1069 #: model:account.account.template,name:l10n_ch.ch_coa_1409 msgid "Ajustement de la valeur des titres" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_6800 #: model:account.account.template,name:l10n_ch.ch_coa_6800 msgid "Amortissements et ajustements de valeur des postes sur immobilisations corporelles" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1519 #: model:account.account.template,name:l10n_ch.ch_coa_1519 msgid "Amortissements sur le mobilier et les installations" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1599 #: model:account.account.template,name:l10n_ch.ch_coa_1599 msgid "Amortissements sur les autres immobilisations corporelles meubles" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1709 #: model:account.account.template,name:l10n_ch.ch_coa_1709 msgid "Amortissements sur les brevets, know-how, licences, droits, dév." msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1609 #: model:account.account.template,name:l10n_ch.ch_coa_1609 msgid "Amortissements sur les immeubles d’exploitation" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1559 #: model:account.account.template,name:l10n_ch.ch_coa_1559 msgid "Amortissements sur les installations de stockage" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1529 #: model:account.account.template,name:l10n_ch.ch_coa_1529 msgid "Amortissements sur les machines de bureau, inf. et syst. comm." msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1509 #: model:account.account.template,name:l10n_ch.ch_coa_1509 msgid "Amortissements sur les machines et appareils" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1549 #: model:account.account.template,name:l10n_ch.ch_coa_1549 msgid "Amortissements sur les outillages et appareils" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1539 #: model:account.account.template,name:l10n_ch.ch_coa_1539 msgid "Amortissements sur les véhicules" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1579 #: model:account.account.template,name:l10n_ch.ch_coa_1579 msgid "Amortissements sur les équipements et installations" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_2270 #: model:account.account.template,name:l10n_ch.ch_coa_2270 msgid "Assurances sociales et institutions de prévoyance" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_6300 #: model:account.account.template,name:l10n_ch.ch_coa_6300 msgid "Assurances-choses, droits, taxes, autorisations" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_5800 #: model:account.account.template,name:l10n_ch.ch_coa_5800 msgid "Autres charges du personnel" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_6700 #: model:account.account.template,name:l10n_ch.ch_coa_6700 msgid "Autres charges d‘exploitation" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1190 #: model:account.account.template,name:l10n_ch.ch_coa_1190 msgid "Autres créances à court terme" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_2210 #: model:account.account.template,name:l10n_ch.ch_coa_2210 msgid "Autres dettes à court terme" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_2140 #: model:account.account.template,name:l10n_ch.ch_coa_2140 msgid "Autres dettes à court terme rémunérées" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_2500 #: model:account.account.template,name:l10n_ch.ch_coa_2500 msgid "Autres dettes à long terme" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1590 #: model:account.account.template,name:l10n_ch.ch_coa_1590 msgid "Autres immobilisations corporelles meubles" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_3600 #: model:account.account.template,name:l10n_ch.ch_coa_3600 msgid "Autres ventes et prestations de services" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1140 #: model:account.account.template,name:l10n_ch.ch_coa_1140 msgid "Avances et prêts" msgstr "" #. module: l10n_ch -#: model:ir.model,name:l10n_ch.model_res_bank -msgid "Bank" -msgstr "" - -#. module: l10n_ch -#: model:ir.model,name:l10n_ch.model_res_partner_bank -msgid "Bank Accounts" -msgstr "" - -#. module: l10n_ch -#: model:ir.model.fields,help:l10n_ch.field_account_config_settings_l10n_ch_isr_print_bank_location -#: model:ir.model.fields,help:l10n_ch.field_res_company_l10n_ch_isr_print_bank_location -msgid "Boolean option field indicating whether or not the alternate layout (the one printing bank name and address) must be used when generating an ISR." -msgstr "" - -#. module: l10n_ch -#: model:ir.model.fields,help:l10n_ch.field_account_invoice_l10n_ch_isr_sent -msgid "Boolean value telling whether or not the ISR corresponding to this invoice has already been printed or sent by mail." -msgstr "" - -#. module: l10n_ch -#: model:ir.model.fields,help:l10n_ch.field_account_invoice_l10n_ch_isr_valid -msgid "Boolean value. True iff all the data required to generate the ISR are present" -msgstr "" - -#. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1700 #: model:account.account.template,name:l10n_ch.ch_coa_1700 msgid "Brevets, know-how, licences, droits, développement" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_2979 #: model:account.account.template,name:l10n_ch.ch_coa_2979 msgid "Bénéfice / perte de l’exercice" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_2970 #: model:account.account.template,name:l10n_ch.ch_coa_2970 msgid "Bénéfice / perte reporté" msgstr "" #. module: l10n_ch -#: model:ir.model.fields,field_description:l10n_ch.field_res_bank_l10n_ch_postal_chf -msgid "CHF ISR reference" -msgstr "" - -#. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1850 #: model:account.account.template,name:l10n_ch.ch_coa_1850 msgid "Capital actions, capital social, droits de participations ou capital de fondation non versés" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_2800 #: model:account.account.template,name:l10n_ch.ch_coa_2800 msgid "Capital-actions, capital social, capital de fondation" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_4521 #: model:account.account.template,name:l10n_ch.ch_coa_4521 msgid "Charbon, briquettes, bois" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_7010 #: model:account.account.template,name:l10n_ch.ch_coa_7010 msgid "Charges accessoires" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_6000 #: model:account.account.template,name:l10n_ch.ch_coa_6000 msgid "Charges de locaux" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_4000 #: model:account.account.template,name:l10n_ch.ch_coa_4000 msgid "Charges de matériel de l‘atelier" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_5900 #: model:account.account.template,name:l10n_ch.ch_coa_5900 msgid "Charges de personnels temporaires" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_6200 #: model:account.account.template,name:l10n_ch.ch_coa_6200 msgid "Charges de véhicules et de transport" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_7510 #: model:account.account.template,name:l10n_ch.ch_coa_7510 msgid "Charges des immeubles d‘exploitation" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_6500 #: model:account.account.template,name:l10n_ch.ch_coa_6500 msgid "Charges d‘administration" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_6400 #: model:account.account.template,name:l10n_ch.ch_coa_6400 msgid "Charges d’énergie et évacuation des déchets" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_6570 #: model:account.account.template,name:l10n_ch.ch_coa_6570 msgid "Charges et leasing d’informatique" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_8500 #: model:account.account.template,name:l10n_ch.ch_coa_8500 msgid "Charges extraordinaires, exceptionnelles ou hors période" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_6900 #: model:account.account.template,name:l10n_ch.ch_coa_6900 msgid "Charges financières" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_8000 #: model:account.account.template,name:l10n_ch.ch_coa_8000 msgid "Charges hors exploitation" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1300 #: model:account.account.template,name:l10n_ch.ch_coa_1300 msgid "Charges payées d‘avance" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_5700 #: model:account.account.template,name:l10n_ch.ch_coa_5700 msgid "Charges sociales" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_2300 #: model:account.account.template,name:l10n_ch.ch_coa_2300 msgid "Charges à payer" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_3803 #: model:account.account.template,name:l10n_ch.ch_coa_3803 msgid "Commissions de tiers" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_4903 #: model:account.account.template,name:l10n_ch.ch_coa_4903 msgid "Commissions obtenues sur achats" msgstr "" @@ -516,147 +413,113 @@ msgid "Companies" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1099 #: model:account.account.template,name:l10n_ch.ch_coa_1099 msgid "Compte d'attente autre" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1091 #: model:account.account.template,name:l10n_ch.ch_coa_1091 msgid "Compte d'attente pour salaires" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_3710 #: model:account.account.template,name:l10n_ch.ch_coa_3710 msgid "Consommations propres" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1269 #: model:account.account.template,name:l10n_ch.ch_coa_1269 msgid "Correction de la valeur de stocks de produits finis" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1279 #: model:account.account.template,name:l10n_ch.ch_coa_1279 msgid "Corrections de la valeur des stock produits semi-ouvrés" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1209 #: model:account.account.template,name:l10n_ch.ch_coa_1209 msgid "Corrections de la valeur des stocks de marchandises" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1289 #: model:account.account.template,name:l10n_ch.ch_coa_1289 msgid "Corrections de la valeur des travaux en cours" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1219 #: model:account.account.template,name:l10n_ch.ch_coa_1219 msgid "Corrections de la valeur sur matières premières" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1180 #: model:account.account.template,name:l10n_ch.ch_coa_1180 msgid "Créances envers les assurances sociales et institutions de prévoyance" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_2000 #: model:account.account.template,name:l10n_ch.ch_coa_2000 msgid "Créanciers" msgstr "" #. module: l10n_ch -#: model:ir.model.fields,field_description:l10n_ch.field_account_invoice_l10n_ch_currency_name -msgid "Currency" -msgstr "" - -#. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_2100 -#: model:account.account,name:l10n_ch.1_ch_coa_2400 #: model:account.account.template,name:l10n_ch.ch_coa_2100 #: model:account.account.template,name:l10n_ch.ch_coa_2400 msgid "Dettes bancaires" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_2160 #: model:account.account.template,name:l10n_ch.ch_coa_2160 msgid "Dettes envers l'actionnaire" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_3806 -#: model:account.account,name:l10n_ch.1_ch_coa_4906 #: model:account.account.template,name:l10n_ch.ch_coa_3806 #: model:account.account.template,name:l10n_ch.ch_coa_4906 msgid "Différences de change" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_2261 #: model:account.account.template,name:l10n_ch.ch_coa_2261 msgid "Dividendes" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_4071 #: model:account.account.template,name:l10n_ch.ch_coa_4071 msgid "Droits de douanes à l'importation" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1109 #: model:account.account.template,name:l10n_ch.ch_coa_1109 msgid "Ducroire" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1100 #: model:account.account.template,name:l10n_ch.ch_coa_1100 msgid "Débiteurs" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_2201 #: model:account.account.template,name:l10n_ch.ch_coa_2201 msgid "Décompte TVA" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_4009 #: model:account.account.template,name:l10n_ch.ch_coa_4009 msgid "Déductions obtenues sur achats" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_3009 #: model:account.account.template,name:l10n_ch.ch_coa_3009 msgid "Déductions sur ventes" msgstr "" #. module: l10n_ch -#: model:ir.model.fields,field_description:l10n_ch.field_res_bank_l10n_ch_postal_eur -msgid "EUR ISR reference" -msgstr "" - -#. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_4540 #: model:account.account.template,name:l10n_ch.ch_coa_4540 msgid "Eau" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_4500 #: model:account.account.template,name:l10n_ch.ch_coa_4500 msgid "Electricité" msgstr "" @@ -667,320 +530,201 @@ msgid "Email Templates" msgstr "" #. module: l10n_ch -#: model:ir.model,name:l10n_ch.model_mail_compose_message -msgid "Email composition wizard" -msgstr "" - -#. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_2450 #: model:account.account.template,name:l10n_ch.ch_coa_2450 msgid "Emprunts" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_2430 #: model:account.account.template,name:l10n_ch.ch_coa_2430 msgid "Emprunts obligataires" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_2120 -#: model:account.account,name:l10n_ch.1_ch_coa_2420 #: model:account.account.template,name:l10n_ch.ch_coa_2120 #: model:account.account.template,name:l10n_ch.ch_coa_2420 msgid "Engagements de financement par leasing" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_6100 #: model:account.account.template,name:l10n_ch.ch_coa_6100 msgid "Entretien, réparations et remplacement des inst. servant à l’exploitation" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1570 #: model:account.account.template,name:l10n_ch.ch_coa_1570 msgid "Equipements et Installations" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_3800 -#: model:account.account,name:l10n_ch.1_ch_coa_4900 #: model:account.account.template,name:l10n_ch.ch_coa_3800 #: model:account.account.template,name:l10n_ch.ch_coa_4900 msgid "Escomptes" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_4530 #: model:account.account.template,name:l10n_ch.ch_coa_4530 msgid "Essence" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_3804 #: model:account.account.template,name:l10n_ch.ch_coa_3804 msgid "Frais d'encaissement" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_3807 #: model:account.account.template,name:l10n_ch.ch_coa_3807 msgid "Frais d'expédition" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_4072 #: model:account.account.template,name:l10n_ch.ch_coa_4072 msgid "Frais de transport à l'achat" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_4070 #: model:account.account.template,name:l10n_ch.ch_coa_4070 msgid "Frêts à l'achat" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_4510 #: model:account.account.template,name:l10n_ch.ch_coa_4510 msgid "Gaz" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1770 #: model:account.account.template,name:l10n_ch.ch_coa_1770 msgid "Goodwill" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1441 -#: model:account.account,name:l10n_ch.1_ch_coa_2451 #: model:account.account.template,name:l10n_ch.ch_coa_1441 #: model:account.account.template,name:l10n_ch.ch_coa_2451 msgid "Hypothèques" msgstr "" #. module: l10n_ch -#: model:ir.actions.report.xml,name:l10n_ch.l10n_ch_isr_report -msgid "ISR" -msgstr "" - -#. module: l10n_ch -#: model:ir.ui.view,arch_db:l10n_ch.l10n_ch_isr_report_template -msgid "ISR for invoice" -msgstr "" - -#. module: l10n_ch -#: model:ir.model.fields,help:l10n_ch.field_account_invoice_l10n_ch_isr_number_spaced -msgid "ISR number split in blocks of 5 characters (right-justified), to generate ISR report." -msgstr "" - -#. module: l10n_ch -#: model:ir.ui.view,arch_db:l10n_ch.isr_invoice_search_view -msgid "ISR reference number" -msgstr "" - -#. module: l10n_ch -#: code:addons/l10n_ch/models/mail_compose_message.py:23 -#, python-format -msgid "ISR sent" -msgstr "" - -#. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1600 #: model:account.account.template,name:l10n_ch.ch_coa_1600 msgid "Immeubles d’exploitation" msgstr "" #. module: l10n_ch -#: model:account.fiscal.position,name:l10n_ch.1_fiscal_position_template_import #: model:account.fiscal.position.template,name:l10n_ch.fiscal_position_template_import msgid "Import/Export" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1176 #: model:account.account.template,name:l10n_ch.ch_coa_1176 msgid "Impôt anticipé" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_2206 #: model:account.account.template,name:l10n_ch.ch_coa_2206 msgid "Impôt anticipé dû" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1171 #: model:account.account.template,name:l10n_ch.ch_coa_1171 msgid "Impôt préalable: TVA s/investissements et autres charges d’exploitation" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1170 #: model:account.account.template,name:l10n_ch.ch_coa_1170 msgid "Impôt préalable: TVA s/matériel, marchandises, prestations et énergie" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1189 -#: model:account.account,name:l10n_ch.1_ch_coa_2279 #: model:account.account.template,name:l10n_ch.ch_coa_1189 #: model:account.account.template,name:l10n_ch.ch_coa_2279 msgid "Impôt à la source" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_2208 -#: model:account.account,name:l10n_ch.1_ch_coa_8900 #: model:account.account.template,name:l10n_ch.ch_coa_2208 #: model:account.account.template,name:l10n_ch.ch_coa_8900 msgid "Impôts directs" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1550 #: model:account.account.template,name:l10n_ch.ch_coa_1550 msgid "Installations de stockage" msgstr "" #. module: l10n_ch -#: model:ir.model,name:l10n_ch.model_account_invoice -msgid "Invoice" -msgstr "" - -#. module: l10n_ch -#: model:ir.model.fields,field_description:l10n_ch.field_account_invoice_l10n_ch_isr_number -msgid "L10N Ch Isr Number" -msgstr "" - -#. module: l10n_ch -#: model:ir.model.fields,field_description:l10n_ch.field_account_invoice_l10n_ch_isr_number_spaced -msgid "L10N Ch Isr Number Spaced" -msgstr "" - -#. module: l10n_ch -#: model:ir.model.fields,field_description:l10n_ch.field_account_invoice_l10n_ch_isr_optical_line -msgid "L10N Ch Isr Optical Line" -msgstr "" - -#. module: l10n_ch -#: model:ir.model.fields,field_description:l10n_ch.field_account_invoice_l10n_ch_isr_postal -msgid "L10N Ch Isr Postal" -msgstr "" - -#. module: l10n_ch -#: model:ir.model.fields,field_description:l10n_ch.field_account_invoice_l10n_ch_isr_postal_formatted -msgid "L10N Ch Isr Postal Formatted" -msgstr "" - -#. module: l10n_ch -#: model:ir.model.fields,field_description:l10n_ch.field_account_invoice_l10n_ch_isr_sent -msgid "L10N Ch Isr Sent" -msgstr "" - -#. module: l10n_ch -#: model:ir.model.fields,field_description:l10n_ch.field_account_invoice_l10n_ch_isr_valid -msgid "L10N Ch Isr Valid" -msgstr "" - -#. module: l10n_ch -#: model:ir.model.fields,field_description:l10n_ch.field_res_partner_bank_l10n_ch_postal -msgid "L10N Ch Postal" -msgstr "" - -#. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_6260 #: model:account.account.template,name:l10n_ch.ch_coa_6260 msgid "Leasing et location de véhicules" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_6105 #: model:account.account.template,name:l10n_ch.ch_coa_6105 msgid "Leasing immobilisations corporelles meubles" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1520 +#: model:account.account.template,name:l10n_ch.l10nch_chart_template_liquidity_transfer +msgid "Liquidity Transfer" +msgstr "" + +#. module: l10n_ch #: model:account.account.template,name:l10n_ch.ch_coa_1520 msgid "Machines de bureau, informatique, systèmes de communication" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1500 #: model:account.account.template,name:l10n_ch.ch_coa_1500 msgid "Machines et appareils" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1200 #: model:account.account.template,name:l10n_ch.ch_coa_1200 msgid "Marchandises commerciales" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1250 #: model:account.account.template,name:l10n_ch.ch_coa_1250 msgid "Marchandises en consignation" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1220 #: model:account.account.template,name:l10n_ch.ch_coa_1220 msgid "Matières auxiliaires" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1230 #: model:account.account.template,name:l10n_ch.ch_coa_1230 msgid "Matières consommables" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1210 #: model:account.account.template,name:l10n_ch.ch_coa_1210 msgid "Matières premières" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_4520 #: model:account.account.template,name:l10n_ch.ch_coa_4520 msgid "Mazout" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1510 #: model:account.account.template,name:l10n_ch.ch_coa_1510 msgid "Mobilier et installations" msgstr "" #. module: l10n_ch -#: model:ir.model.fields,help:l10n_ch.field_account_invoice_l10n_ch_isr_optical_line -msgid "Optical reading line, as it will be printed on ISR" -msgstr "" - -#. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1540 #: model:account.account.template,name:l10n_ch.ch_coa_1540 msgid "Outillages et appareils" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1480 #: model:account.account.template,name:l10n_ch.ch_coa_1480 msgid "Participations" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_4086 #: model:account.account.template,name:l10n_ch.ch_coa_4086 msgid "Pertes de matières" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_3805 #: model:account.account.template,name:l10n_ch.ch_coa_3805 msgid "Pertes sur créances clients, variation ducroire" msgstr "" @@ -991,182 +735,126 @@ msgid "Plan comptable 2015" msgstr "" #. module: l10n_ch -#: model:ir.model.fields,help:l10n_ch.field_account_invoice_l10n_ch_isr_postal_formatted -msgid "Postal reference of the bank, formated with '-' and without the padding zeros, to generate ISR report." -msgstr "" - -#. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_4400 #: model:account.account.template,name:l10n_ch.ch_coa_4400 msgid "Prestations / travaux de tiers" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_3700 #: model:account.account.template,name:l10n_ch.ch_coa_3700 msgid "Prestations propres" msgstr "" #. module: l10n_ch -#: model:ir.ui.view,arch_db:l10n_ch.isr_invoice_form -msgid "Print ISR" -msgstr "" - -#. module: l10n_ch -#: model:ir.model.fields,field_description:l10n_ch.field_res_company_l10n_ch_isr_print_bank_location -msgid "Print bank location" -msgstr "" - -#. module: l10n_ch -#: model:ir.model.fields,field_description:l10n_ch.field_account_config_settings_l10n_ch_isr_print_bank_location -msgid "Print bank on ISR" -msgstr "" - -#. module: l10n_ch -#: model:ir.ui.view,arch_db:l10n_ch.l10n_ch-isr_account_config_settings -msgid "Print the coordinates of your bank under the 'Payment for' title of the ISR.\n" -" Your address will be moved to the 'in favour of' section." -msgstr "" - -#. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_7000 #: model:account.account.template,name:l10n_ch.ch_coa_7000 msgid "Produits accessoires" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_7500 #: model:account.account.template,name:l10n_ch.ch_coa_7500 msgid "Produits des immeubles d‘exploitation" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_2301 #: model:account.account.template,name:l10n_ch.ch_coa_2301 msgid "Produits encaissés d’avance" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_8510 #: model:account.account.template,name:l10n_ch.ch_coa_8510 msgid "Produits extraordinaires, exceptionnels ou hors période" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_6950 #: model:account.account.template,name:l10n_ch.ch_coa_6950 msgid "Produits financiers" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_8100 #: model:account.account.template,name:l10n_ch.ch_coa_8100 msgid "Produits hors exploitation" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1301 #: model:account.account.template,name:l10n_ch.ch_coa_1301 msgid "Produits à recevoir" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_2980 #: model:account.account.template,name:l10n_ch.ch_coa_2980 msgid "Propres actions, parts sociales, droits de participations (poste négatif)" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_2600 #: model:account.account.template,name:l10n_ch.ch_coa_2600 msgid "Provisions" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_2330 #: model:account.account.template,name:l10n_ch.ch_coa_2330 msgid "Provisions à court terme" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1440 #: model:account.account.template,name:l10n_ch.ch_coa_1440 msgid "Prêts" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_6600 #: model:account.account.template,name:l10n_ch.ch_coa_6600 msgid "Publicité" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_3801 #: model:account.account.template,name:l10n_ch.ch_coa_3801 msgid "Rabais et réduction de prix" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_4901 #: model:account.account.template,name:l10n_ch.ch_coa_4901 msgid "Rabais et réductions de prix" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_3802 -#: model:account.account,name:l10n_ch.1_ch_coa_4092 #: model:account.account.template,name:l10n_ch.ch_coa_3802 #: model:account.account.template,name:l10n_ch.ch_coa_4092 msgid "Ristournes" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_2940 #: model:account.account.template,name:l10n_ch.ch_coa_2940 msgid "Réserves d‘évaluation" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_2960 #: model:account.account.template,name:l10n_ch.ch_coa_2960 msgid "Réserves libres" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_2950 #: model:account.account.template,name:l10n_ch.ch_coa_2950 msgid "Réserves légales issues du bénéfice" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_2900 #: model:account.account.template,name:l10n_ch.ch_coa_2900 msgid "Réserves légales issues du capital" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_5000 #: model:account.account.template,name:l10n_ch.ch_coa_5000 msgid "Salaires" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1260 #: model:account.account.template,name:l10n_ch.ch_coa_1260 msgid "Stocks de produits finis" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1270 #: model:account.account.template,name:l10n_ch.ch_coa_1270 msgid "Stocks de produits semi-ouvrés" msgstr "" -#. module: l10n_ch -#: model:ir.ui.menu,name:l10n_ch.account_reports_ch_statements_menu -msgid "Swiss Statements" -msgstr "" - #. module: l10n_ch #: model:account.account.tag,name:l10n_ch.vat_tag_dedouanement msgid "Switzerland VAT Form: 100% dédouanement (base in grid 400)" @@ -1208,13 +896,13 @@ msgid "Switzerland VAT Form: grid 280" msgstr "" #. module: l10n_ch -#: model:account.account.tag,name:l10n_ch.vat_tag_301_a -msgid "Switzerland VAT Form: grid 301 base" +#: model:account.account.tag,name:l10n_ch.vat_tag_302_a +msgid "Switzerland VAT Form: grid 302 base" msgstr "" #. module: l10n_ch -#: model:account.account.tag,name:l10n_ch.vat_tag_301_b -msgid "Switzerland VAT Form: grid 301 tax" +#: model:account.account.tag,name:l10n_ch.vat_tag_302_b +msgid "Switzerland VAT Form: grid 302 tax" msgstr "" #. module: l10n_ch @@ -1228,13 +916,13 @@ msgid "Switzerland VAT Form: grid 311 tax" msgstr "" #. module: l10n_ch -#: model:account.account.tag,name:l10n_ch.vat_tag_341_a -msgid "Switzerland VAT Form: grid 341 base" +#: model:account.account.tag,name:l10n_ch.vat_tag_342_a +msgid "Switzerland VAT Form: grid 342 base" msgstr "" #. module: l10n_ch -#: model:account.account.tag,name:l10n_ch.vat_tag_341_b -msgid "Switzerland VAT Form: grid 341 tax" +#: model:account.account.tag,name:l10n_ch.vat_tag_342_b +msgid "Switzerland VAT Form: grid 342 tax" msgstr "" #. module: l10n_ch @@ -1247,6 +935,16 @@ msgstr "" msgid "Switzerland VAT Form: grid 381 tax" msgstr "" +#. module: l10n_ch +#: model:account.account.tag,name:l10n_ch.vat_tag_382_a +msgid "Switzerland VAT Form: grid 382 base" +msgstr "" + +#. module: l10n_ch +#: model:account.account.tag,name:l10n_ch.vat_tag_382_b +msgid "Switzerland VAT Form: grid 382 tax" +msgstr "" + #. module: l10n_ch #: model:account.account.tag,name:l10n_ch.vat_tag_400 msgid "Switzerland VAT Form: grid 400" @@ -1273,297 +971,228 @@ msgid "Switzerland VAT Form: grid 420" msgstr "" #. module: l10n_ch -#: model:account.tax,name:l10n_ch.1_vat_O_import +#: model:account.tax.group,name:l10n_ch.tax_group_tva_0 +msgid "TVA 0%" +msgstr "" + +#. module: l10n_ch #: model:account.tax.template,name:l10n_ch.vat_O_import msgid "TVA 0% Importations de biens et services" msgstr "" #. module: l10n_ch -#: model:account.tax,name:l10n_ch.1_vat_O_exclude #: model:account.tax.template,name:l10n_ch.vat_O_exclude msgid "TVA 0% exclue" msgstr "" #. module: l10n_ch -#: model:account.tax,name:l10n_ch.1_vat_25_purchase_incl +#: model:account.tax.group,name:l10n_ch.tax_group_tva_25 +msgid "TVA 2.5%" +msgstr "" + +#. module: l10n_ch #: model:account.tax.template,name:l10n_ch.vat_25_purchase_incl msgid "TVA 2.5% sur achat B&S (Incl. TR)" msgstr "" #. module: l10n_ch -#: model:account.tax,name:l10n_ch.1_vat_25_purchase #: model:account.tax.template,name:l10n_ch.vat_25_purchase msgid "TVA 2.5% sur achat B&S (TR)" msgstr "" #. module: l10n_ch -#: model:account.tax,name:l10n_ch.1_vat_25_invest_incl #: model:account.tax.template,name:l10n_ch.vat_25_invest_incl msgid "TVA 2.5% sur invest. et autres ch. (Incl. TR)" msgstr "" #. module: l10n_ch -#: model:account.tax,name:l10n_ch.1_vat_25_invest #: model:account.tax.template,name:l10n_ch.vat_25_invest msgid "TVA 2.5% sur invest. et autres ch. (TR)" msgstr "" #. module: l10n_ch -#: model:account.tax,name:l10n_ch.1_vat_38_purchase_incl -#: model:account.tax.template,name:l10n_ch.vat_38_purchase_incl -msgid "TVA 3.8% sur achat B&S (Incl. TS)" +#: model:account.tax.group,name:l10n_ch.tax_group_tva_37 +msgid "TVA 3.7%" msgstr "" #. module: l10n_ch -#: model:account.tax,name:l10n_ch.1_vat_38_purchase -#: model:account.tax.template,name:l10n_ch.vat_38_purchase -msgid "TVA 3.8% sur achat B&S (TS)" +#: model:account.tax.template,name:l10n_ch.vat_37_purchase_incl +msgid "TVA 3.7% sur achat B&S (Incl. TS)" msgstr "" #. module: l10n_ch -#: model:account.tax,name:l10n_ch.1_vat_38_invest_incl -#: model:account.tax.template,name:l10n_ch.vat_38_invest_incl -msgid "TVA 3.8% sur invest. et autres ch. (Incl. TS)" +#: model:account.tax.template,name:l10n_ch.vat_37_purchase +msgid "TVA 3.7% sur achat B&S (TS)" msgstr "" #. module: l10n_ch -#: model:account.tax,name:l10n_ch.1_vat_38_invest -#: model:account.tax.template,name:l10n_ch.vat_38_invest -msgid "TVA 3.8% sur invest. et autres ch. (TS)" +#: model:account.tax.template,name:l10n_ch.vat_37_invest_incl +msgid "TVA 3.7% sur invest. et autres ch. (Incl. TS)" msgstr "" #. module: l10n_ch -#: model:account.tax,name:l10n_ch.1_vat_80_purchase_incl -#: model:account.tax.template,name:l10n_ch.vat_80_purchase_incl -msgid "TVA 8.0% sur achat B&S (Incl. TN)" +#: model:account.tax.template,name:l10n_ch.vat_37_invest +msgid "TVA 3.7% sur invest. et autres ch. (TS)" msgstr "" #. module: l10n_ch -#: model:account.tax,name:l10n_ch.1_vat_80_purchase -#: model:account.tax.template,name:l10n_ch.vat_80_purchase -msgid "TVA 8.0% sur achat B&S (TN)" +#: model:account.tax.group,name:l10n_ch.tax_group_tva_77 +msgid "TVA 7.7%" msgstr "" #. module: l10n_ch -#: model:account.tax,name:l10n_ch.1_vat_80_invest_incl -#: model:account.tax.template,name:l10n_ch.vat_80_invest_incl -msgid "TVA 8.0% sur invest. et autres ch. (Incl. TN)" +#: model:account.tax.template,name:l10n_ch.vat_77_purchase_incl +msgid "TVA 7.7% sur achat B&S (Incl. TN)" msgstr "" #. module: l10n_ch -#: model:account.tax,name:l10n_ch.1_vat_80_invest -#: model:account.tax.template,name:l10n_ch.vat_80_invest -msgid "TVA 8.0% sur invest. et autres ch. (TN)" +#: model:account.tax.template,name:l10n_ch.vat_77_purchase +msgid "TVA 7.7% sur achat B&S (TN)" +msgstr "" + +#. module: l10n_ch +#: model:account.tax.template,name:l10n_ch.vat_77_purchase_reverse +msgid "TVA 7.7% sur achat service a l'etranger (reverse charge)" +msgstr "" + +#. module: l10n_ch +#: model:account.tax.template,name:l10n_ch.vat_77_invest_incl +msgid "TVA 7.7% sur invest. et autres ch. (Incl. TN)" +msgstr "" + +#. module: l10n_ch +#: model:account.tax.template,name:l10n_ch.vat_77_invest +msgid "TVA 7.7% sur invest. et autres ch. (TN)" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_2200 #: model:account.account.template,name:l10n_ch.ch_coa_2200 msgid "TVA due" msgstr "" #. module: l10n_ch -#: model:account.tax,name:l10n_ch.1_vat_XO #: model:account.tax.template,name:l10n_ch.vat_XO msgid "TVA due a 0% (Exportations)" msgstr "" #. module: l10n_ch -#: model:account.tax,name:l10n_ch.1_vat_25 #: model:account.tax.template,name:l10n_ch.vat_25 msgid "TVA due a 2.5% (TR)" msgstr "" #. module: l10n_ch -#: model:account.tax,name:l10n_ch.1_vat_38 -#: model:account.tax.template,name:l10n_ch.vat_38 -msgid "TVA due a 3.8% (TS)" +#: model:account.tax.template,name:l10n_ch.vat_37 +msgid "TVA due a 3.7% (TS)" msgstr "" #. module: l10n_ch -#: model:account.tax,name:l10n_ch.1_vat_80 -#: model:account.tax.template,name:l10n_ch.vat_80 -msgid "TVA due a 8.0% (TN)" +#: model:account.tax.template,name:l10n_ch.vat_77 +msgid "TVA due a 7.7% (TN)" +msgstr "" + +#. module: l10n_ch +#: model:account.tax.template,name:l10n_ch.vat_77_purchase_return +msgid "TVA due a 7.7% (TN) (return)" msgstr "" #. module: l10n_ch -#: model:account.tax,name:l10n_ch.1_vat_25_incl #: model:account.tax.template,name:l10n_ch.vat_25_incl msgid "TVA due à 2.5% (Incl. TR)" msgstr "" #. module: l10n_ch -#: model:account.tax,name:l10n_ch.1_vat_38_incl -#: model:account.tax.template,name:l10n_ch.vat_38_incl -msgid "TVA due à 3.8% (Incl. TS)" +#: model:account.tax.template,name:l10n_ch.vat_37_incl +msgid "TVA due à 3.7% (Incl. TS)" msgstr "" #. module: l10n_ch -#: model:account.tax,name:l10n_ch.1_vat_80_incl -#: model:account.tax.template,name:l10n_ch.vat_80_incl -msgid "TVA due à 8.0% (Incl. TN)" +#: model:account.tax.template,name:l10n_ch.vat_77_incl +msgid "TVA due à 7.7% (Incl. TN)" msgstr "" #. module: l10n_ch -#: model:ir.model.fields,help:l10n_ch.field_res_partner_bank_l10n_ch_postal -msgid "The ISR number of the company within the bank" -msgstr "" - -#. module: l10n_ch -#: model:ir.model.fields,help:l10n_ch.field_account_invoice_l10n_ch_currency_name -msgid "The name of this invoice's currency" -msgstr "" - -#. module: l10n_ch -#: model:ir.model.fields,help:l10n_ch.field_account_invoice_l10n_ch_isr_postal -msgid "The postal reference identifying the bank managing this ISR." -msgstr "" - -#. module: l10n_ch -#: model:ir.model.fields,help:l10n_ch.field_res_bank_l10n_ch_postal_chf -msgid "The postal reference of the bank, used to generate ISR payment slips in CHF." -msgstr "" - -#. module: l10n_ch -#: model:ir.model.fields,help:l10n_ch.field_res_bank_l10n_ch_postal_eur -msgid "The postal reference of the bank, used to generate ISR payment slips in EUR." -msgstr "" - -#. module: l10n_ch -#: model:ir.model.fields,help:l10n_ch.field_account_invoice_l10n_ch_isr_number -msgid "The reference number associated with this invoice" -msgstr "" - -#. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1060 #: model:account.account.template,name:l10n_ch.ch_coa_1060 msgid "Titres" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1400 #: model:account.account.template,name:l10n_ch.ch_coa_1400 msgid "Titres à long terme" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_transfer_account_id -#: model:account.account.template,name:l10n_ch.transfer_account_id -msgid "Transferts internes" -msgstr "" - -#. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1280 #: model:account.account.template,name:l10n_ch.ch_coa_1280 msgid "Travaux en cours" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_3940 #: model:account.account.template,name:l10n_ch.ch_coa_3940 msgid "Variation de la valeur des prestations non facturées" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1287 #: model:account.account.template,name:l10n_ch.ch_coa_1287 msgid "Variation de la valeur des travaux en cours" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1277 #: model:account.account.template,name:l10n_ch.ch_coa_1277 msgid "Variation de stock produits semi-ouvrés" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1267 #: model:account.account.template,name:l10n_ch.ch_coa_1267 msgid "Variation de stocks de produits finis" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1207 -#: model:account.account,name:l10n_ch.1_ch_coa_4800 #: model:account.account.template,name:l10n_ch.ch_coa_1207 #: model:account.account.template,name:l10n_ch.ch_coa_4800 msgid "Variation des stocks de marchandises" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_4801 #: model:account.account.template,name:l10n_ch.ch_coa_4801 msgid "Variation des stocks de matières premières" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_3901 #: model:account.account.template,name:l10n_ch.ch_coa_3901 msgid "Variation des stocks de produits finis" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_3900 #: model:account.account.template,name:l10n_ch.ch_coa_3900 msgid "Variation des stocks de produits semi-finis" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1217 #: model:account.account.template,name:l10n_ch.ch_coa_1217 msgid "Variation des stocks des matières premières" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_4008 -#: model:account.account,name:l10n_ch.1_ch_coa_4080 #: model:account.account.template,name:l10n_ch.ch_coa_4008 #: model:account.account.template,name:l10n_ch.ch_coa_4080 msgid "Variations de stocks" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_3200 #: model:account.account.template,name:l10n_ch.ch_coa_3200 msgid "Ventes de marchandises" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_3400 #: model:account.account.template,name:l10n_ch.ch_coa_3400 msgid "Ventes de prestations" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_3000 #: model:account.account.template,name:l10n_ch.ch_coa_3000 msgid "Ventes de produits fabriqués" msgstr "" #. module: l10n_ch -#: model:account.account,name:l10n_ch.1_ch_coa_1530 #: model:account.account.template,name:l10n_ch.ch_coa_1530 msgid "Véhicules" msgstr "" - -#. module: l10n_ch -#: code:addons/l10n_ch/models/account_invoice.py:153 -#, python-format -msgid "You cannot generate an ISR yet.\n" -"\n" -" For this, you need to :\n" -"\n" -" - set a valid postal account number (or an IBAN referencing one) for your company\n" -"\n" -" - define its bank\n" -"\n" -" - associate this bank with a postal reference for the currency used in this invoice\n" -"\n" -" - fill the 'bank account' field of the invoice with the postal to be used to receive the related payment. A default account will be automatically set for all invoices created after you defined a postal account for your company." -msgstr "" - -#. module: l10n_ch -#: model:ir.model,name:l10n_ch.model_account_config_settings -msgid "account.config.settings" -msgstr "" - diff --git a/addons/l10n_it/__manifest__.py b/addons/l10n_it/__manifest__.py index f78432b06b3..fd621d872fd 100644 --- a/addons/l10n_it/__manifest__.py +++ b/addons/l10n_it/__manifest__.py @@ -17,7 +17,7 @@ Piano dei conti italiano di un'impresa generica. Italian accounting chart and localization. """, 'category': 'Localization', - 'website': 'http://www.openerp-italia.org/', + 'website': 'http://www.odoo.com/', 'data': [ 'data/l10n_it_chart_data.xml', 'data/account.account.template.csv', diff --git a/addons/payment_ccavenue/__init__.py b/addons/l10n_it_edi/__init__.py similarity index 50% rename from addons/payment_ccavenue/__init__.py rename to addons/l10n_it_edi/__init__.py index e52ad6f83e3..90b17558ffc 100644 --- a/addons/payment_ccavenue/__init__.py +++ b/addons/l10n_it_edi/__init__.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -from . import controllers from . import models -from odoo.addons.payment.models.payment_acquirer import create_missing_journal_for_acquirers +from . import tests diff --git a/addons/l10n_it_edi/__manifest__.py b/addons/l10n_it_edi/__manifest__.py new file mode 100644 index 00000000000..fc24a276e68 --- /dev/null +++ b/addons/l10n_it_edi/__manifest__.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +{ + 'name': 'Italy - E-invoicing', + 'version': '0.3', + 'depends': ['l10n_it'], + 'author': 'Odoo', + 'description': """ +E-invoice implementation + """, + 'category': 'Localization', + 'website': 'http://www.odoo.com/', + 'data': [ + 'security/ir.model.access.csv', + 'data/invoice_it_template.xml', + 'views/l10n_it_view.xml', + ], + 'demo': [ + 'data/account_invoice_demo.xml', + ], +} diff --git a/addons/l10n_it_edi/data/account_invoice_demo.xml b/addons/l10n_it_edi/data/account_invoice_demo.xml new file mode 100644 index 00000000000..c68a3871df5 --- /dev/null +++ b/addons/l10n_it_edi/data/account_invoice_demo.xml @@ -0,0 +1,23 @@ + + + + + + IT00410123456 + 0123456789987654 + RF01 + 12345 + + + + IT11111111123 + + + IT11111111234 + + + IT11111112345 + + + + diff --git a/addons/l10n_it_edi/data/invoice_it_template.xml b/addons/l10n_it_edi/data/invoice_it_template.xml new file mode 100644 index 00000000000..c3af915c58c --- /dev/null +++ b/addons/l10n_it_edi/data/invoice_it_template.xml @@ -0,0 +1,175 @@ + + + + + + + + + + diff --git a/addons/l10n_it_edi/models/__init__.py b/addons/l10n_it_edi/models/__init__.py new file mode 100644 index 00000000000..a7b2d212437 --- /dev/null +++ b/addons/l10n_it_edi/models/__init__.py @@ -0,0 +1,8 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from . import res_partner +from . import res_company +from . import account_invoice +from . import ir_mail_server +from . import ddt diff --git a/addons/l10n_it_edi/models/account_invoice.py b/addons/l10n_it_edi/models/account_invoice.py new file mode 100644 index 00000000000..1ba9b6ec213 --- /dev/null +++ b/addons/l10n_it_edi/models/account_invoice.py @@ -0,0 +1,758 @@ +# -*- coding:utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +import base64 +import zipfile +import io +import logging +import re + +from datetime import date, datetime +from lxml import etree + +from odoo import api, fields, models, _ +from odoo.tools import float_repr +from odoo.exceptions import UserError, ValidationError +from odoo.addons.base.models.ir_mail_server import MailDeliveryException +from odoo.tests.common import Form + + +_logger = logging.getLogger(__name__) + +DEFAULT_FACTUR_ITALIAN_DATE_FORMAT = '%Y-%m-%d' + +class AccountInvoice(models.Model): + _inherit = 'account.invoice' + _name = 'account.invoice' + + l10n_it_send_state = fields.Selection([ + ('new', 'New'), + ('other', 'Other'), + ('to_send', 'Not yet send'), + ('sent', 'Sent, waiting for response'), + ('invalid', 'Sent, but invalid'), + ('delivered', 'This invoice is delivered'), + ('delivered_accepted', 'This invoice is delivered and accepted by destinatory'), + ('delivered_refused', 'This invoice is delivered and refused by destinatory'), + ('delivered_expired', 'This invoice is delivered and expired (expiry of the maximum term for communication of acceptance/refusal)'), + ('failed_delivery', 'Delivery impossible, ES certify that it has received the invoice and that the file \ + could not be delivered to the addressee') # ok we must do nothing + ], default='to_send', copy=False) + + l10n_it_stamp_duty = fields.Float(default=0, string="Dati Bollo", size=15, readonly=True, states={'draft': [('readonly', False)]}) + + l10n_it_ddt_id = fields.Many2one('l10n_it.ddt', string='DDT', readonly=True, states={'draft': [('readonly', False)]}, copy=False) + + l10n_it_einvoice_name = fields.Char(readonly=True, copy=False) + + l10n_it_einvoice_id = fields.Many2one('ir.attachment', string="Electronic invoice", copy=False) + + @api.multi + def invoice_validate(self): + # Clean context from default_type to avoid making attachment + # with wrong values in subsequent operations + cleaned_ctx = dict(self.env.context) + cleaned_ctx.pop('default_type', None) + self = self.with_context(cleaned_ctx) + + super(AccountInvoice, self).invoice_validate() + for invoice in self: + if invoice.company_id.country_id != self.env.ref('base.it'): + continue + if invoice.type == 'in_invoice' or invoice.type == 'in_refund': + invoice.l10n_it_send_state = "other" + continue + + invoice._check_before_xml_exporting() + + invoice.invoice_generate_xml() + if len(invoice.commercial_partner_id.l10n_it_pa_index or '') == 6: + invoice.message_post( + body=(_("Invoices for PA are not managed by Odoo, you can download the document and send it on your own.")) + ) + invoice.l10n_it_send_state = "other" + continue + invoice.l10n_it_send_state = "to_send" + invoice.send_pec_mail() + + def _check_before_xml_exporting(self): + seller = self.company_id + buyer = self.commercial_partner_id + + # <1.1.1.1> + if not seller.country_id: + raise UserError(_("%s must have a country") % (seller.display_name)) + + # <1.1.1.2> + if not seller.vat: + raise UserError(_("%s must have a VAT number") % (seller.display_name)) + elif len(seller.vat) > 30: + raise UserError(_("The maximum length for VAT number is 30. %s have a VAT number too long: %s.") % (seller.display_name, seller.vat)) + + # <1.2.1.2> + if not seller.l10n_it_codice_fiscale: + raise UserError(_("%s must have a codice fiscale number") % (seller.display_name)) + + # <1.2.1.8> + if not seller.l10n_it_tax_system: + raise UserError("The seller's company must have a tax system.") + + # <1.2.2> + if not seller.street and not seller.street2: + raise UserError(_("%s must have a street.") % (seller.display_name)) + if not seller.zip: + raise UserError(_("%s must have a post code.") % (seller.display_name)) + if len(seller.zip) != 5 and seller.country_id.code == 'IT': + raise UserError(_("%s must have a post code of length 5.") % (seller.display_name)) + if not seller.city: + raise UserError(_("%s must have a city.") % (seller.display_name)) + if not seller.country_id: + raise UserError(_("%s must have a country.") % (seller.display_name)) + + # <1.4.1> + if not buyer.vat and not buyer.l10n_it_codice_fiscale: + raise UserError(_("The buyer, %s, or his company must have either a VAT number either a tax code (Codice Fiscale).") % (buyer.display_name)) + + # <1.4.2> + if not buyer.street and not buyer.street2: + raise UserError(_("%s must have a street.") % (buyer.display_name)) + if not buyer.zip: + raise UserError(_("%s must have a post code.") % (buyer.display_name)) + if len(buyer.zip) != 5 and buyer.country_id.code == 'IT': + raise UserError(_("%s must have a post code of length 5.") % (buyer.display_name)) + if not buyer.city: + raise UserError(_("%s must have a city.") % (buyer.display_name)) + if not buyer.country_id: + raise UserError(_("%s must have a country.") % (buyer.display_name)) + + # <2.2.1> + for invoice_line in self.invoice_line_ids: + if len(invoice_line.invoice_line_tax_ids) != 1: + raise UserError(_("You must select one and only one tax by line.")) + + for tax_line in self.tax_line_ids: + if not tax_line.tax_id.l10n_it_has_exoneration and tax_line.tax_id.amount == 0: + raise ValidationError(_("%s has an amount of 0.0, you must indicate the kind of exoneration." % tax_line.name)) + + @api.multi + def invoice_generate_xml(self): + for invoice in self: + if invoice.l10n_it_einvoice_id and invoice.l10n_it_send_state not in ['invalid', 'to_send']: + raise UserError(_("You can't regenerate an E-Invoice when the first one is sent and there are no errors")) + if invoice.l10n_it_einvoice_id: + invoice.l10n_it_einvoice_id.unlink() + + a = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" + n = invoice.id + progressive_number = "" + while n: + (n,m) = divmod(n,len(a)) + progressive_number = a[m] + progressive_number + + report_name = '%(country_code)s%(codice)s_%(progressive_number)s.xml' % { + 'country_code': invoice.company_id.country_id.code, + 'codice': invoice.company_id.l10n_it_codice_fiscale, + 'progressive_number': progressive_number.zfill(5), + } + invoice.l10n_it_einvoice_name = report_name + + data = b"" + invoice._export_as_xml() + description = _('Italian invoice: %s') % invoice.type + invoice.l10n_it_einvoice_id = self.env['ir.attachment'].create({ + 'name': report_name, + 'res_id': invoice.id, + 'res_model': invoice._name, + 'datas': base64.encodestring(data), + 'datas_fname': report_name, + 'description': description, + 'type': 'binary', + }) + + invoice.message_post( + body=(_("E-Invoice is generated on %s by %s") % (fields.Datetime.now(), self.env.user.display_name)) + ) + + def _export_as_xml(self): + ''' Create the xml file content. + :return: The XML content as str. + ''' + self.ensure_one() + + def format_date(dt): + # Format the date in the italian standard. + dt = dt or datetime.now() + return dt.strftime(DEFAULT_FACTUR_ITALIAN_DATE_FORMAT) + + def format_monetary(number, currency): + # Format the monetary values to avoid trailing decimals (e.g. 90.85000000000001). + return float_repr(number, min(2, currency.decimal_places)) + + def format_numbers(number): + #format number to str with between 2 and 8 decimals (event if it's .00) + number_splited = str(number).split('.') + if len(number_splited) == 1: + return "%.02f" % number + + cents = number_splited[1] + if len(cents) > 8: + return "%.08f" % number + return float_repr(number, max(2, len(cents))) + + def format_numbers_two(number): + #format number to str with 2 (event if it's .00) + return "%.02f" % number + + def discount_type(discount): + return 'SC' if discount > 0 else 'MG' + + def format_phone(number): + if not number: + return False + number = number.replace(' ', '').replace('/', '').replace('.', '') + if len(number) > 4 and len(number) < 13: + return number + return False + + def get_vat_number(vat): + return vat[2:].replace(' ', '') + + def get_vat_country(vat): + return vat[:2].upper() + + formato_trasmissione = "FPR12" + if len(self.commercial_partner_id.l10n_it_pa_index or '1') == 6: + formato_trasmissione = "FPA12" + + if self.type == 'out_invoice': + document_type = 'TD01' + elif self.type == 'out_refund': + document_type = 'TD04' + else: + document_type = 'TD0X' + + pdf = self.env.ref('account.account_invoices').render_qweb_pdf(self.id)[0] + pdf = base64.b64encode(pdf) + pdf_name = re.sub(r'\W+', '', self.number) + '.pdf' + + # Create file content. + template_values = { + 'record': self, + 'format_date': format_date, + 'format_monetary': format_monetary, + 'format_numbers': format_numbers, + 'format_numbers_two': format_numbers_two, + 'format_phone': format_phone, + 'discount_type': discount_type, + 'get_vat_number': get_vat_number, + 'get_vat_country': get_vat_country, + 'abs': abs, + 'formato_trasmissione': formato_trasmissione, + 'document_type': document_type, + 'pdf': pdf, + 'pdf_name': pdf_name, + } + content = self.env.ref('l10n_it_edi.account_invoice_it_FatturaPA_export').render(template_values) + return content + + @api.multi + def send_pec_mail(self): + self.ensure_one() + allowed_state = ['to_send', 'invalid'] + + if not self.company_id.l10n_it_mail_pec_server_id or not self.company_id.l10n_it_address_send_fatturapa: + self.message_post( + body=(_("Error when sending mail with E-Invoice: Your company must have a mail PEC server and must indicate the mail PEC that will send electronic invoice.")) + ) + self.l10n_it_send_state = 'invalid' + return + + if self.l10n_it_send_state not in allowed_state: + raise UserError(_("%s isn't in a right state. It must be in a 'Not yet send' or 'Invalid' state.") % (self.display_name)) + + message = self.env['mail.message'].create({ + 'subject': _('Sending file: %s') % (self.l10n_it_einvoice_id.name), + 'body': _('Sending file: %s to ES: %s') % (self.l10n_it_einvoice_id.name, self.env.user.company_id.l10n_it_address_recipient_fatturapa), + 'email_from': self.env.user.company_id.l10n_it_address_send_fatturapa, + 'mail_server_id': self.env.user.company_id.l10n_it_mail_pec_server_id.id, + 'attachment_ids': [(6, 0, self.l10n_it_einvoice_id.ids)], + }) + + mail_fattura = self.env['mail.mail'].create({ + 'mail_message_id': message.id, + 'email_to': self.env.user.company_id.l10n_it_address_recipient_fatturapa, + }) + try: + mail_fattura.send(raise_exception=True) + self.message_post( + body=(_("Mail sent on %s by %s") % (fields.Datetime.now(), self.env.user.display_name)) + ) + self.l10n_it_send_state = 'sent' + except MailDeliveryException as error: + self.message_post( + body=(_("Error when sending mail with E-Invoice: %s") % (error.args[0])) + ) + self.l10n_it_send_state = 'invalid' + + def _import_xml_invoice(self, content, attachment): + ''' Extract invoice values from the E-Invoice xml tree passed as parameter. + + :param content: The tree of the xml file. + :return: A dictionary containing account.invoice values to create/update it. + ''' + + try: + tree = etree.fromstring(content) + except: + _logger.info('Error during decoding XML file') + return self.env['account.invoice'] + + invoices = self.env['account.invoice'] + + # possible to have multiple invoices in the case of an invoice batch, the batch itself is repeated for every invoice of the batch + for body_tree in tree.xpath('//FatturaElettronicaBody', namespaces=tree.nsmap): + + elements = tree.xpath('//DatiGeneraliDocumento/TipoDocumento', namespaces=tree.nsmap) + if elements and elements[0].text and elements[0].text == 'TD01': + self_ctx = self.with_context(type='in_invoice') + elif elements and elements[0].text and elements[0].text == 'TD04': + self_ctx = self.with_context(type='in_refund') + else: + _logger.info(_('Document type not managed: %s.') % (elements[0].text)) + + # type must be present in the context to get the right behavior of the _default_journal method (account.invoice). + # journal_id must be present in the context to get the right behavior of the _default_account method (account.invoice.line). + + elements = tree.xpath('//CessionarioCommittente//IdCodice', namespaces=tree.nsmap) + company = elements and self.env['res.company'].search([('vat', 'ilike', elements[0].text)], limit=1) + if not company: + elements = tree.xpath('//CessionarioCommittente//CodiceFiscale', namespaces=tree.nsmap) + company = elements and self.env['res.company'].search([('l10n_it_codice_fiscale', 'ilike', elements[0].text)], limit=1) + + if company: + self_ctx = self_ctx.with_context(company_id=company.id) + else: + company = self.env.user.company_id + if elements: + _logger.info(_('Company not found with codice fiscale: %s. The company\'s user is set by default.') % elements[0].text) + else: + _logger.info(_('Company not found. The company\'s user is set by default.')) + + if not self.env.user._is_superuser(): + if self.env.user.company_id != company: + raise UserError(_("You can only import invoice concern your current company: %s") % self.env.user.company_id.display_name) + + journal_id = self_ctx._default_journal().id + self_ctx = self_ctx.with_context(journal_id=journal_id) + + # self could be a single record (editing) or be empty (new). + with Form(self_ctx, view='account.invoice_supplier_form') as invoice_form: + message_to_log = [] + + invoice_form.company_id = company + + # Refund type. + # TD01 == invoice + # TD02 == advance/down payment on invoice + # TD03 == advance/down payment on fee + # TD04 == credit note + # TD05 == debit note + # TD06 == fee + elements = tree.xpath('//DatiGeneraliDocumento/TipoDocumento', namespaces=tree.nsmap) + if elements and elements[0].text and elements[0].text == 'TD01': + invoice_form.type = 'in_invoice' + elif elements and elements[0].text and elements[0].text == 'TD04': + invoice_form.type = 'in_refund' + + # Partner (first step to avoid warning 'Warning! You must first select a partner.'). <1.2> + elements = tree.xpath('//CedentePrestatore//IdCodice', namespaces=tree.nsmap) + partner = elements and self.env['res.partner'].search(['&', ('vat', 'ilike', elements[0].text), '|', ('company_id', '=', company.id), ('company_id', '=', False)], limit=1) + if not partner: + elements = tree.xpath('//CedentePrestatore//CodiceFiscale', namespaces=tree.nsmap) + partner = elements and self.env['res.partner'].search(['&', ('l10n_it_codice_fiscale', '=', elements[0].text), '|', ('company_id', '=', company.id), ('company_id', '=', False)], limit=1) + if not partner: + elements = tree.xpath('//DatiTrasmissione//Email', namespaces=tree.nsmap) + partner = elements and self.env['res.partner'].search(['&', '|', ('email', '=', elements[0].text), ('l10n_it_pec_email', '=', elements[0].text), '|', ('company_id', '=', company.id), ('company_id', '=', False)], limit=1) + if partner: + invoice_form.partner_id = partner + else: + message_to_log.append("%s
%s" % ( + _("Vendor not found, useful informations from XML file:"), + self._compose_info_message( + tree, './/CedentePrestatore'))) + + # Numbering attributed by the transmitter. <1.1.2> + elements = tree.xpath('//ProgressivoInvio', namespaces=tree.nsmap) + if elements: + invoice_form.name = elements[0].text + + elements = body_tree.xpath('.//DatiGeneraliDocumento//Numero', namespaces=body_tree.nsmap) + if elements: + invoice_form.reference = elements[0].text + + # Currency. <2.1.1.2> + elements = body_tree.xpath('.//DatiGeneraliDocumento/Divisa', namespaces=body_tree.nsmap) + if elements: + currency_str = elements[0].text + currency = self.env.ref('base.%s' % currency_str.upper(), raise_if_not_found=False) + if currency != self.env.user.company_id.currency_id and currency.active: + invoice_form.currency_id = currency + + # Date. <2.1.1.3> + elements = body_tree.xpath('.//DatiGeneraliDocumento/Data', namespaces=body_tree.nsmap) + if elements: + date_str = elements[0].text + date_obj = datetime.strptime(date_str, DEFAULT_FACTUR_ITALIAN_DATE_FORMAT) + invoice_form.date_invoice = date_obj.strftime(DEFAULT_FACTUR_ITALIAN_DATE_FORMAT) + + # Dati Bollo. <2.1.1.6> + elements = body_tree.xpath('.//DatiGeneraliDocumento/DatiBollo/ImportoBollo', namespaces=body_tree.nsmap) + if elements: + invoice_form.l10n_it_stamp_duty = float(elements[0].text) + + # List of all amount discount (will be add after all article to avoid to have a negative sum) + discount_list = [] + percentage_global_discount = 1.0 + + # Global discount. <2.1.1.8> + discount_elements = body_tree.xpath('.//DatiGeneraliDocumento/ScontoMaggiorazione', namespaces=body_tree.nsmap) + total_discount_amount = 0.0 + if discount_elements: + for discount_element in discount_elements: + discount_line = discount_element.xpath('.//Tipo', namespaces=body_tree.nsmap) + discount_sign = -1 + if discount_line and discount_line[0].text == 'SC': + discount_sign = 1 + discount_percentage = discount_element.xpath('.//Percentuale', namespaces=body_tree.nsmap) + if discount_percentage and discount_percentage[0].text: + percentage_global_discount *= 1 - float(discount_percentage[0].text)/100 * discount_sign + + discount_amount_text = discount_element.xpath('.//Importo', namespaces=body_tree.nsmap) + if discount_amount_text and discount_amount_text[0].text: + discount_amount = float(discount_amount_text[0].text) * discount_sign * -1 + discount = {} + discount["seq"] = 0 + + if discount_amount < 0: + discount["name"] = _('GLOBAL DISCOUNT') + else: + discount["name"] = _('GLOBAL EXTRA CHARGE') + discount["amount"] = discount_amount + discount["tax"] = [] + discount_list.append(discount) + + # Comment. <2.1.1.11> + elements = body_tree.xpath('.//DatiGeneraliDocumento//Causale', namespaces=body_tree.nsmap) + for element in elements: + invoice_form.comment = '%s%s\n' % (invoice_form.comment or '', element.text) + + # Informations relative to the purchase order, the contract, the agreement, + # the reception phase or invoices previously transmitted + # <2.1.2> - <2.1.6> + for document_type in ['DatiOrdineAcquisto', 'DatiContratto', 'DatiConvenzione', 'DatiRicezione', 'DatiFattureCollegate']: + elements = body_tree.xpath('.//DatiGenerali/' + document_type, namespaces=body_tree.nsmap) + if elements: + for element in elements: + message_to_log.append("%s %s
%s" % (document_type, _("from XML file:"), + self._compose_info_message(element, '.'))) + + # Dati DDT. <2.1.8> + elements = body_tree.xpath('.//DatiGenerali/DatiDDT', namespaces=body_tree.nsmap) + if elements: + message_to_log.append("%s
%s" % ( + _("Transport informations from XML file:"), + self._compose_info_message(body_tree, './/DatiGenerali/DatiDDT'))) + + # Due date. <2.4.2.5> + elements = body_tree.xpath('.//DatiPagamento/DettaglioPagamento/DataScadenzaPagamento', namespaces=body_tree.nsmap) + if elements: + date_str = elements[0].text + date_obj = datetime.strptime(date_str, DEFAULT_FACTUR_ITALIAN_DATE_FORMAT) + invoice_form.date_due = fields.Date.to_string(date_obj) + + # Total amount. <2.4.2.6> + elements = body_tree.xpath('.//ImportoPagamento', namespaces=body_tree.nsmap) + amount_total_import = 0 + for element in elements: + amount_total_import += float(element.text) + if amount_total_import: + message_to_log.append(_("Total amount from the XML File: %s") % ( + amount_total_import)) + + # Bank account. <2.4.2.13> + elements = body_tree.xpath('.//DatiPagamento/DettaglioPagamento/IBAN', namespaces=body_tree.nsmap) + if elements: + if invoice_form.partner_id and invoice_form.partner_id.commercial_partner_id: + bank = self.env['res.partner.bank'].search([ + ('acc_number', '=', elements[0].text), + ('partner_id.id', '=', invoice_form.partner_id.commercial_partner_id.id) + ]) + else: + bank = self.env['res.partner.bank'].search([('acc_number', '=', elements[0].text)]) + if bank: + invoice_form.partner_bank_id = bank + else: + message_to_log.append("%s
%s" % ( + _("Bank account not found, useful informations from XML file:"), + self._compose_multi_info_message( + body_tree, ['.//DatiPagamento//Beneficiario', + './/DatiPagamento//IstitutoFinanziario', + './/DatiPagamento//IBAN', + './/DatiPagamento//ABI', + './/DatiPagamento//CAB', + './/DatiPagamento//BIC', + './/DatiPagamento//ModalitaPagamento']))) + else: + elements = body_tree.xpath('.//DatiPagamento/DettaglioPagamento', namespaces=body_tree.nsmap) + if elements: + message_to_log.append("%s
%s" % ( + _("Bank account not found, useful informations from XML file:"), + self._compose_info_message(body_tree, './/DatiPagamento'))) + + # Invoice lines. <2.2.1> + elements = body_tree.xpath('.//DettaglioLinee', namespaces=body_tree.nsmap) + if elements: + for element in elements: + with invoice_form.invoice_line_ids.new() as invoice_line_form: + + # Sequence. + line_elements = element.xpath('.//NumeroLinea', namespaces=body_tree.nsmap) + if line_elements: + invoice_line_form.sequence = int(line_elements[0].text) * 2 + + # Product. + line_elements = element.xpath('.//Descrizione', namespaces=body_tree.nsmap) + if line_elements: + invoice_line_form.name = " ".join(line_elements[0].text.split()) + + elements_code = element.xpath('.//CodiceArticolo', namespaces=body_tree.nsmap) + if elements_code: + for element_code in elements_code: + type_code = element_code.xpath('.//CodiceTipo', namespaces=body_tree.nsmap)[0] + code = element_code.xpath('.//CodiceValore', namespaces=body_tree.nsmap)[0] + if type_code.text == 'EAN': + product = self.env['product.product'].search([('barcode', '=', code.text)]) + if product: + invoice_line_form.product_id = product + break + if partner: + product_supplier = self.env['product.supplierinfo'].search([('name', '=', partner.id), ('product_code', '=', code.text)]) + if product_supplier and product_supplier.product_id: + invoice_line_form.product_id = product_supplier.product_id + break + if not invoice_line_form.product_id: + for element_code in elements_code: + code = element_code.xpath('.//CodiceValore', namespaces=body_tree.nsmap)[0] + product = self.env['product.product'].search([('default_code', '=', code.text)]) + if product: + invoice_line_form.product_id = product + break + + # Price Unit. + line_elements = element.xpath('.//PrezzoUnitario', namespaces=body_tree.nsmap) + if line_elements: + invoice_line_form.price_unit = float(line_elements[0].text) + + # Quantity. + line_elements = element.xpath('.//Quantita', namespaces=body_tree.nsmap) + if line_elements: + invoice_line_form.quantity = float(line_elements[0].text) + else: + invoice_line_form.quantity = 1 + + # Taxes + tax_element = element.xpath('.//AliquotaIVA', namespaces=body_tree.nsmap) + natura_element = element.xpath('.//Natura', namespaces=body_tree.nsmap) + invoice_line_form.invoice_line_tax_ids.clear() + if tax_element and tax_element[0].text: + percentage = float(tax_element[0].text) + if natura_element and natura_element[0].text: + l10n_it_kind_exoneration = natura_element[0].text + tax = self.env['account.tax'].search([ + ('company_id', '=', invoice_form.company_id.id), + ('amount_type', '=', 'percent'), + ('type_tax_use', '=', 'purchase'), + ('amount', '=', percentage), + ('l10n_it_kind_exoneration', '=', l10n_it_kind_exoneration), + ], limit=1) + else: + tax = self.env['account.tax'].search([ + ('company_id', '=', invoice_form.company_id.id), + ('amount_type', '=', 'percent'), + ('type_tax_use', '=', 'purchase'), + ('amount', '=', percentage), + ], limit=1) + l10n_it_kind_exoneration = '' + + if tax: + invoice_line_form.invoice_line_tax_ids.add(tax) + else: + if l10n_it_kind_exoneration: + message_to_log.append(_("Tax not found with percentage: %s and exoneration %s for the article: %s") % ( + percentage, + l10n_it_kind_exoneration, + invoice_line_form.name)) + else: + message_to_log.append(_("Tax not found with percentage: %s for the article: %s") % ( + percentage, + invoice_line_form.name)) + + # Discount in cascade mode. + # if 3 discounts : -10% -50€ -20% + # the result must be : (((price -10%)-50€) -20%) + # Generic form : (((price -P1%)-A1€) -P2%) + # It will be split in two parts: fix amount and pourcent amount + # example: (((((price - A1€) -P2%) -A3€) -A4€) -P5€) + # pourcent: 1-(1-P2)*(1-P5) + # fix amount: A1*(1-P2)*(1-P5)+A3*(1-P5)+A4*(1-P5) (we must take account of all + # percentage present after the fix amount) + line_elements = element.xpath('.//ScontoMaggiorazione', namespaces=body_tree.nsmap) + total_discount_amount = 0.0 + total_discount_percentage = percentage_global_discount + if line_elements: + for line_element in line_elements: + discount_line = line_element.xpath('.//Tipo', namespaces=body_tree.nsmap) + discount_sign = -1 + if discount_line and discount_line[0].text == 'SC': + discount_sign = 1 + discount_percentage = line_element.xpath('.//Percentuale', namespaces=body_tree.nsmap) + if discount_percentage and discount_percentage[0].text: + pourcentage_actual = 1 - float(discount_percentage[0].text)/100 * discount_sign + total_discount_percentage *= pourcentage_actual + total_discount_amount *= pourcentage_actual + + discount_amount = line_element.xpath('.//Importo', namespaces=body_tree.nsmap) + if discount_amount and discount_amount[0].text: + total_discount_amount += float(discount_amount[0].text) * discount_sign * -1 + + # Save amount discount. + if total_discount_amount != 0: + discount = {} + discount["seq"] = invoice_line_form.sequence + 1 + + if total_discount_amount < 0: + discount["name"] = _('DISCOUNT: ') + invoice_line_form.name + else: + discount["name"] = _('EXTRA CHARGE: ') + invoice_line_form.name + discount["amount"] = total_discount_amount + discount["tax"] = [] + for tax in invoice_line_form.invoice_line_tax_ids: + discount["tax"].append(tax) + discount_list.append(discount) + invoice_line_form.discount = (1 - total_discount_percentage) * 100 + + # Apply amount discount. + for discount in discount_list: + with invoice_form.invoice_line_ids.new() as invoice_line_form_discount: + invoice_line_form_discount.invoice_line_tax_ids.clear() + invoice_line_form_discount.sequence = discount["seq"] + invoice_line_form_discount.name = discount["name"] + invoice_line_form_discount.price_unit = discount["amount"] + + new_invoice = invoice_form.save() + new_invoice.l10n_it_send_state = "other" + + elements = body_tree.xpath('.//Allegati', namespaces=body_tree.nsmap) + if elements: + for element in elements: + name_attachment = element.xpath('.//NomeAttachment', namespaces=body_tree.nsmap)[0].text + attachment_64 = str.encode(element.xpath('.//Attachment', namespaces=body_tree.nsmap)[0].text) + attachment_64 = self.env['ir.attachment'].create({ + 'name': name_attachment, + 'datas': attachment_64, + 'datas_fname': name_attachment, + 'type': 'binary', + }) + + # default_res_id is had to context to avoid facturx to import his content + new_invoice.with_context(default_res_id=new_invoice.id).message_post( + body=(_("Attachment from XML")), + attachment_ids=[attachment_64.id] + ) + + for message in message_to_log: + new_invoice.message_post(body=message) + + if attachment: + new_invoice.l10n_it_einvoice_name = attachment.name + attachment.write({'res_model': 'account.invoice', 'res_id': new_invoice.id}) + new_invoice.message_post(attachment_ids=[attachment.id]) + invoices += new_invoice + return invoices + + def _compose_info_message(self, tree, element_tags): + output_str = "" + elements = tree.xpath(element_tags, namespaces=tree.nsmap) + for element in elements: + output_str += "
    " + for line in element.iter(): + if line.text: + text = " ".join(line.text.split()) + if text: + output_str += "
  • %s: %s
  • " % (line.tag, text) + output_str += "
" + return output_str + + def _compose_multi_info_message(self, tree, element_tags): + output_str = "
    " + + for element_tag in element_tags: + elements = tree.xpath(element_tag, namespaces=tree.nsmap) + if not elements: + continue + for element in elements: + text = " ".join(element.text.split()) + if text: + output_str += "
  • %s: %s
  • " % (element.tag, text) + return output_str + "
" + +class AccountTax(models.Model): + _name = "account.tax" + _inherit = "account.tax" + + l10n_it_vat_due_date = fields.Selection([ + ("I", "[I] IVA ad esigibilità immediata"), + ("D", "[D] IVA ad esigibilità differita"), + ("S", "[S] Scissione dei pagamenti")], default="I", string="VAT due date") + + l10n_it_has_exoneration = fields.Boolean(string="Has exoneration of tax (Italy)", help="Tax has a tax exoneration.") + l10n_it_kind_exoneration = fields.Selection(selection=[ + ("N1", "[N1] Escluse ex art. 15"), + ("N2", "[N2] Non soggette"), + ("N3", "[N3] Non imponibili"), + ("N4", "[N4] Esenti"), + ("N5", "[N5] Regime del margine / IVA non esposta in fattura"), + ("N6", "[N6] Inversione contabile (per le operazioni in reverse charge ovvero nei casi di autofatturazione per acquisti extra UE di servizi ovvero per importazioni di beni nei soli casi previsti)"), + ("N7", "[N7] IVA assolta in altro stato UE (vendite a distanza ex art. 40 c. 3 e 4 e art. 41 c. 1 lett. b, DL 331/93; prestazione di servizi di telecomunicazioni, tele-radiodiffusione ed elettronici ex art. 7-sexies lett. f, g, art. 74-sexies DPR 633/72)")], + string="Exoneration", + help="Exoneration type", + default="N1") + l10n_it_law_reference = fields.Char(string="Law Reference", size=100) + + @api.constrains('l10n_it_has_exoneration', + 'l10n_it_kind_exoneration', + 'l10n_it_law_reference', + 'amount', + 'l10n_it_vat_due_date') + def _check_exoneration_with_no_tax(self): + for tax in self: + if tax.l10n_it_has_exoneration: + if not tax.l10n_it_kind_exoneration or not tax.l10n_it_law_reference or tax.amount != 0: + raise ValidationError("If the tax has exoneration, you must enter a kind of exoneration, a law reference and the amount of the tax must be 0.0.") + if tax.l10n_it_kind_exoneration == 'N6' and tax.l10n_it_vat_due_date == 'S': + raise UserError(_("'Scissione dei pagamenti' is not compatible with exoneration of kind 'N6'")) + +class ImportInvoiceImportWizard(models.TransientModel): + _name = 'account.invoice.import.wizard' + _inherit = 'account.invoice.import.wizard' + + @api.multi + def _create_invoice_from_file(self, attachment): + if attachment.mimetype == 'application/xml' and re.search("([A-Z]{2}[A-Za-z0-9]{2,28}_[A-Za-z0-9]{0,5}.(xml.p7m|xml))", attachment.name): + if self.env['account.invoice'].search([('l10n_it_einvoice_name', '=', attachment.name)], limit=1): + # invoice already exist + raise UserError(_('E-invoice already exist: %s') % attachment.name) + self = self.with_context(default_journal_id= self.journal_id.id) + invoice = self.env['account.invoice']._import_xml_invoice(base64.decodestring(attachment.datas), attachment) + else: + invoice = super(ImportInvoiceImportWizard, self)._create_invoice_from_file(attachment) + return invoice diff --git a/addons/l10n_it_edi/models/ddt.py b/addons/l10n_it_edi/models/ddt.py new file mode 100644 index 00000000000..61464a7d76a --- /dev/null +++ b/addons/l10n_it_edi/models/ddt.py @@ -0,0 +1,21 @@ +# -*- coding:utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import fields, models, api + +class L10nItDdt(models.Model): + _name = 'l10n_it.ddt' + _description = 'Transport Document' + + invoice_id = fields.One2many('account.invoice', 'l10n_it_ddt_id', string='Invoice Reference', + ondelete='cascade') + + name = fields.Char(string="Numero DDT", size=20, help="Transport document number", required=True) + date = fields.Date(string="Data DDT", help="Transport document date", required=True) + + @api.multi + def name_get(self): + res = [] + for ddt in self: + res.append((ddt.id, ("%s (%s)") % (ddt.name, ddt.date))) + return res diff --git a/addons/l10n_it_edi/models/ir_mail_server.py b/addons/l10n_it_edi/models/ir_mail_server.py new file mode 100644 index 00000000000..3bfce47100d --- /dev/null +++ b/addons/l10n_it_edi/models/ir_mail_server.py @@ -0,0 +1,358 @@ +# -*- coding:utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +import zipfile +import io +import re +import logging +import email +import dateutil +import pytz +import base64 +try: + from xmlrpc import client as xmlrpclib +except ImportError: + import xmlrpclib + + +from lxml import etree +from datetime import datetime + +from odoo import api, fields, models, tools, _ +from odoo.exceptions import ValidationError + + +_logger = logging.getLogger(__name__) + +class FetchmailServer(models.Model): + _name = 'fetchmail.server' + _inherit = 'fetchmail.server' + + l10n_it_is_pec = fields.Boolean('PEC server', help="If PEC Server, only mail from '...@pec.fatturapa.it' will be processed.") + l10n_it_last_uid = fields.Integer(string='Last message UID', default=1) + + @api.constrains('l10n_it_is_pec', 'type') + def _check_pec(self): + for record in self: + if record.l10n_it_is_pec and record.type != 'imap': + raise ValidationError("PEC mail server must be of type IMAP.") + + @api.multi + def fetch_mail(self): + """ WARNING: meant for cron usage only - will commit() after each email! """ + + MailThread = self.env['mail.thread'] + for server in self.filtered(lambda s: s.l10n_it_is_pec): + _logger.info('start checking for new emails on %s PEC server %s', server.type, server.name) + + count, failed = 0, 0 + imap_server = None + try: + imap_server = server.connect() + imap_server.select() + + result, data = imap_server.uid('search', None, '(FROM "@pec.fatturapa.it")', '(UID %s:*)' % (server.l10n_it_last_uid)) + new_max_uid = server.l10n_it_last_uid + for uid in data[0].split(): + if int(uid) <= server.l10n_it_last_uid: + # We get always minimum 1 message. If no new message, we receive the newest already managed. + continue + + result, data = imap_server.uid('fetch', uid, '(RFC822)') + + if not data[0]: + continue + message = data[0][1] + + # To leave the mail in the state in which they were. + if "Seen" not in data[1].decode("utf-8"): + imap_server.uid('STORE', uid, '+FLAGS', '\\Seen') + else: + imap_server.uid('STORE', uid, '-FLAGS', '\\Seen') + + # See details in message_process() in mail_thread.py + if isinstance(message, xmlrpclib.Binary): + message = bytes(message.data) + if isinstance(message, str): + message = message.encode('utf-8') + msg_txt = email.message_from_bytes(message) + + try: + self._attachment_invoice(msg_txt) + new_max_uid = max(new_max_uid, int(uid)) + except Exception: + _logger.info('Failed to process mail from %s server %s.', server.type, server.name, exc_info=True) + failed += 1 + self._cr.commit() + count += 1 + server.write({'l10n_it_last_uid': new_max_uid}) + _logger.info("Fetched %d email(s) on %s server %s; %d succeeded, %d failed.", count, server.type, server.name, (count - failed), failed) + except Exception: + _logger.info("General failure when trying to fetch mail from %s server %s.", server.type, server.name, exc_info=True) + finally: + if imap_server: + imap_server.close() + imap_server.logout() + server.write({'date': fields.Datetime.now()}) + return super(FetchmailServer, self.filtered(lambda s: not s.l10n_it_is_pec)).fetch_mail() + + def _attachment_invoice(self, msg_txt): + body, attachments = self.env['mail.thread']._message_extract_payload(msg_txt) + from_address = tools.decode_smtp_header(msg_txt.get('from')) + for attachment in attachments: + split_attachment = attachment.fname.rpartition('.') + if len(split_attachment) < 3: + _logger.info('E-invoice filename not compliant: %s', attachment.fname) + continue + attachment_name = split_attachment[0] + attachment_ext = split_attachment[2] + split_underscore = attachment_name.rsplit('_', 2) + if len(split_underscore) < 2: + _logger.info('E-invoice filename not compliant: %s', attachment.fname) + continue + + if attachment_ext != 'zip': + if split_underscore[1] in ['RC', 'NS', 'MC', 'MT', 'EC', 'SE', 'NE', 'DT']: + # we have a receipt + self._message_receipt_invoice(split_underscore[1], attachment) + elif re.search("([A-Z]{2}[A-Za-z0-9]{2,28}_[A-Za-z0-9]{0,5}.(xml.p7m|xml))", attachment.fname): + # we have a new E-invoice + self._create_invoice_from_mail(attachment.content, attachment.fname, from_address) + else: + if split_underscore[1] == 'AT': + # Attestazione di avvenuta trasmissione della fattura con impossibilità di recapito + self._message_AT_invoice(attachment) + else: + _logger.info('New E-invoice in zip file: %s', attachment.fname) + self._create_invoice_from_mail_with_zip(attachment, from_address) + + def _create_invoice_from_mail(self, att_content, att_name, from_address): + if self.env['account.invoice'].search([('l10n_it_einvoice_name', '=', att_name)], limit=1): + # invoice already exist + _logger.info('E-invoice already exist: %s', att_name) + return + + invoice_attachment = self.env['ir.attachment'].create({ + 'name': att_name, + 'datas': base64.encodestring(att_content), + 'datas_fname': att_name, + 'type': 'binary', + }) + + invoice = self.env['account.invoice']._import_xml_invoice(att_content, invoice_attachment) + invoice.l10n_it_send_state = "new" + invoice.source_email = from_address + self._cr.commit() + + _logger.info('New E-invoice: %s', att_name) + + + def _create_invoice_from_mail_with_zip(self, attachment_zip, from_address): + with zipfile.ZipFile(io.BytesIO(attachment_zip.content)) as z: + for att_name in z.namelist(): + if self.env['account.invoice'].search([('l10n_it_einvoice_name', '=', att_name)], limit=1): + # invoice already exist + _logger.info('E-invoice in zip file (%s) already exist: %s', attachment_zip.fname, att_name) + continue + att_content = z.open(att_name).read() + + self._create_invoice_from_mail(att_content, att_name, from_address) + + def _message_AT_invoice(self, attachment_zip): + with zipfile.ZipFile(io.BytesIO(attachment_zip.content)) as z: + for attachment_name in z.namelist(): + split_name_attachment = attachment_name.rpartition('.') + if len(split_name_attachment) < 3: + continue + split_underscore = split_name_attachment[0].rsplit('_', 2) + if len(split_underscore) < 2: + continue + if split_underscore[1] == 'AT': + attachment = z.open(attachment_name).read() + _logger.info('New AT receipt for: %s', split_underscore[0]) + try: + tree = etree.fromstring(attachment) + except: + _logger.info('Error in decoding new receipt file: %s', attachment_name) + return + + elements = tree.xpath('//NomeFile', namespaces=tree.nsmap) + if elements and elements[0].text: + filename = elements[0].text + else: + return + + related_invoice = self.env['account.invoice'].search([ + ('l10n_it_einvoice_name', '=', filename)]) + if not related_invoice: + _logger.info('Error: invoice not found for receipt file: %s', filename) + return + + related_invoice.l10n_it_send_state = 'failed_delivery' + info = self._return_multi_line_xml(tree, ['//IdentificativoSdI', '//DataOraRicezione', '//MessageId', '//PecMessageId', '//Note']) + related_invoice.message_post( + body=(_("ES certify that it has received the invoice and that the file \ + could not be delivered to the addressee.
%s") % (info)) + ) + + def _message_receipt_invoice(self, receipt_type, attachment): + try: + tree = etree.fromstring(attachment.content) + except: + _logger.info('Error in decoding new receipt file: %s', attachment.fname) + return {} + + elements = tree.xpath('//NomeFile', namespaces=tree.nsmap) + if elements and elements[0].text: + filename = elements[0].text + else: + return {} + + if receipt_type == 'RC': + # Delivery receipt + # This is the receipt sent by the ES to the transmitting subject to communicate + # delivery of the file to the addressee + related_invoice = self.env['account.invoice'].search([ + ('l10n_it_einvoice_name', '=', filename), + ('l10n_it_send_state', '=', 'sent')]) + if not related_invoice: + _logger.info('Error: invoice not found for receipt file: %s', attachment.fname) + return + related_invoice.l10n_it_send_state = 'delivered' + info = self._return_multi_line_xml(tree, ['//IdentificativoSdI', '//DataOraRicezione', '//DataOraConsegna', '//Note']) + related_invoice.message_post( + body=(_("E-Invoice is delivery to the destinatory:
%s") % (info)) + ) + + elif receipt_type == 'NS': + # Rejection notice + # This is the receipt sent by the ES to the transmitting subject if one or more of + # the checks carried out by the ES on the file received do not have a successful result. + related_invoice = self.env['account.invoice'].search([ + ('l10n_it_einvoice_name', '=', filename), + ('l10n_it_send_state', '=', 'sent')]) + if not related_invoice: + _logger.info('Error: invoice not found for receipt file: %s', attachment.fname) + return + related_invoice.l10n_it_send_state = 'invalid' + error = self._return_error_xml(tree) + related_invoice.message_post( + body=(_("Errors in the E-Invoice :
%s") % (error)) + ) + activity_vals = { + 'activity_type_id': self.env.ref('mail.mail_activity_data_todo').id, + 'user_id': related_invoice.user_id.id if related_invoice.user_id else self.env.user.id + } + related_invoice.activity_schedule(summary='Rejection notice', **activity_vals) + + elif receipt_type == 'MC': + # Failed delivery notice + # This is the receipt sent by the ES to the transmitting subject if the file is not + # delivered to the addressee. + related_invoice = self.env['account.invoice'].search([ + ('l10n_it_einvoice_name', '=', filename), + ('l10n_it_send_state', '=', 'sent')]) + if not related_invoice: + _logger.info('Error: invoice not found for receipt file: %s', attachment.fname) + return + info = self._return_multi_line_xml(tree, [ + '//IdentificativoSdI', + '//DataOraRicezione', + '//Descrizione', + '//MessageId', + '//Note']) + related_invoice.message_post( + body=(_("The E-invoice is not delivered to the addressee. The Exchange System is\ + unable to deliver the file to the Public Administration. The Exchange System will\ + contact the PA to report the problem and request that they provide a solution. \ + During the following 15 days, the Exchange System will try to forward the FatturaPA\ + file to the Administration in question again. More informations:
%s") % (info)) + ) + + elif receipt_type == 'NE': + # Outcome notice + # This is the receipt sent by the ES to the invoice sender to communicate the result + # (acceptance or refusal of the invoice) of the checks carried out on the document by + # the addressee. + related_invoice = self.env['account.invoice'].search([ + ('l10n_it_einvoice_name', '=', filename), + ('l10n_it_send_state', '=', 'delivered')]) + if not related_invoice: + _logger.info('Error: invoice not found for receipt file: %s', attachment.fname) + return + elements = tree.xpath('//Esito', namespaces=tree.nsmap) + if elements and elements[0].text: + if elements[0].text == 'EC01': + related_invoice.l10n_it_send_state = 'delivered_accepted' + elif elements[0].text == 'EC02': + related_invoice.l10n_it_send_state = 'delivered_refused' + + info = self._return_multi_line_xml(tree, + ['//Esito', + '//Descrizione', + '//IdentificativoSdI', + '//DataOraRicezione', + '//DataOraConsegna', + '//Note' + ]) + related_invoice.message_post( + body=(_("Outcome notice: %s
%s") % (related_invoice.l10n_it_send_state, info)) + ) + if related_invoice.l10n_it_send_state == 'delivered_refused': + activity_vals = { + 'activity_type_id': self.env.ref('mail.mail_activity_data_todo').id, + 'user_id': related_invoice.user_id.id if related_invoice.user_id else self.env.user.id + } + related_invoice.activity_schedule(summary='Outcome notice: Refused', **activity_vals) + + # elif receipt_type == 'MT': + # Metadata file + # This is the file sent by the ES to the addressee together with the invoice file, + # containing the main reference data of the file useful for processing, including + # the IdentificativoSDI. + # Useless for Odoo + + elif receipt_type == 'DT': + # Deadline passed notice + # This is the receipt sent by the ES to both the invoice sender and the invoice + # addressee to communicate the expiry of the maximum term for communication of + # acceptance/refusal. + related_invoice = self.env['account.invoice'].search([ + ('l10n_it_einvoice_name', '=', filename), ('l10n_it_send_state', '=', 'delivered')]) + if not related_invoice: + _logger.info('Error: invoice not found for receipt file: %s', attachment.fname) + return + related_invoice.l10n_it_send_state = 'delivered_expired' + info = self._return_multi_line_xml(tree, [ + '//Descrizione', + '//IdentificativoSdI', + '//Note']) + related_invoice.message_post( + body=(_("Expiration of the maximum term for communication of acceptance/refusal:\ + %s
%s") % (filename, info)) + ) + + def _return_multi_line_xml(self, tree, element_tags): + output_str = "
    " + + for element_tag in element_tags: + elements = tree.xpath(element_tag, namespaces=tree.nsmap) + if not elements: + continue + for element in elements: + text = " ".join(element.text.split()) + if text: + output_str += "
  • %s: %s
  • " % (element.tag, text) + return output_str + "
" + + def _return_error_xml(self, tree): + output_str = "
    " + + elements = tree.xpath('//Errore', namespaces=tree.nsmap) + if not elements: + return + for element in elements: + descrizione = " ".join(element[1].text.split()) + if descrizione: + output_str += "
  • Errore %s: %s
  • " % (element[0].text, descrizione) + return output_str + "
" diff --git a/addons/l10n_it_edi/models/res_company.py b/addons/l10n_it_edi/models/res_company.py new file mode 100644 index 00000000000..a0cce53b9a5 --- /dev/null +++ b/addons/l10n_it_edi/models/res_company.py @@ -0,0 +1,110 @@ +# -*- coding:utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import api, fields, models +from odoo.exceptions import ValidationError + +TAX_SYSTEM = [ + ("RF01", "[RF01] Ordinario"), + ("RF02", "[RF02] Contribuenti minimi (art.1, c.96-117, L. 244/07)"), + ("RF04", "[RF04] Agricoltura e attività connesse e pesca (artt.34 e 34-bis, DPR 633/72)"), + ("RF05", "[RF05] Vendita sali e tabacchi (art.74, c.1, DPR. 633/72)"), + ("RF06", "[RF06] Commercio fiammiferi (art.74, c.1, DPR 633/72)"), + ("RF07", "[RF07] Editoria (art.74, c.1, DPR 633/72)"), + ("RF08", "[RF08] Gestione servizi telefonia pubblica (art.74, c.1, DPR 633/72)"), + ("RF09", "[RF09] Rivendita documenti di trasporto pubblico e di sosta (art.74, c.1, DPR 633/72)"), + ("RF10", "[RF10] Intrattenimenti, giochi e altre attività di cui alla tariffa allegata al DPR 640/72 (art.74, c.6, DPR 633/72)"), + ("RF11", "[RF11] Agenzie viaggi e turismo (art.74-ter, DPR 633/72)"), + ("RF12", "[RF12] Agriturismo (art.5, c.2, L. 413/91)"), + ("RF13", "[RF13] Vendite a domicilio (art.25-bis, c.6, DPR 600/73)"), + ("RF14", "[RF14] Rivendita beni usati, oggetti d’arte, d’antiquariato o da collezione (art.36, DL 41/95)"), + ("RF15", "[RF15] Agenzie di vendite all’asta di oggetti d’arte, antiquariato o da collezione (art.40-bis, DL 41/95)"), + ("RF16", "[RF16] IVA per cassa P.A. (art.6, c.5, DPR 633/72)"), + ("RF17", "[RF17] IVA per cassa (art. 32-bis, DL 83/2012)"), + ("RF18", "[RF18] Altro"), + ("RF19", "[RF19] Regime forfettario (art.1, c.54-89, L. 190/2014)"), +] + +class ResCompany(models.Model): + _name = 'res.company' + _inherit = 'res.company' + + l10n_it_codice_fiscale = fields.Char(string="Codice Fiscale", size=16, related='partner_id.l10n_it_codice_fiscale', + store=True, readonly=False, help="Fiscal code of your company") + l10n_it_tax_system = fields.Selection(selection=TAX_SYSTEM, string="Tax System", + help="Please select the Tax system to which you are subjected.") + + # PEC server + l10n_it_mail_pec_server_id = fields.Many2one('ir.mail_server', string="Server PEC", + help="Configure your PEC-mail server to send electronic invoices.") + l10n_it_address_recipient_fatturapa = fields.Char(string="Government PEC-mail", + help="Enter Government PEC-mail address. Ex: sdi01@pec.fatturapa.it") + l10n_it_address_send_fatturapa = fields.Char(string="Company PEC-mail", + help="Enter your company PEC-mail address. Ex: yourcompany@pec.mail.it") + + + # Economic and Administrative Index + l10n_it_has_eco_index = fields.Boolean(default=False, + help="The seller/provider is a company listed on the register of companies and as\ + such must also indicate the registration data on all documents (art. 2250, Italian\ + Civil Code)") + l10n_it_eco_index_office = fields.Many2one('res.country.state', domain="[('country_id','=','IT')]", + string="Province of the register-of-companies office") + l10n_it_eco_index_number = fields.Char(string="Number in register of companies", size=20, + help="This field must contain the number under which the\ + seller/provider is listed on the register of companies.") + l10n_it_eco_index_share_capital = fields.Float(default="0.0", size=15, string="Share capital actually paid up", + help="Mandatory if the seller/provider is a company with share\ + capital (SpA, SApA, Srl), this field must contain the amount\ + of share capital actually paid up as resulting from the last\ + financial statement") + l10n_it_eco_index_sole_shareholder = fields.Selection( + [ + ("NO", "Not a limited liability company"), + ("SU", "Socio unico"), + ("SM", "Più soci")], + string="Shareholder") + l10n_it_eco_index_liquidation_state = fields.Selection( + [ + ("LS", "The company is in a state of liquidation"), + ("LN", "The company is not in a state of liquidation")], + string="Liquidation state") + + + # Tax representative + l10n_it_has_tax_representative = fields.Boolean(default=False, + help="The seller/provider is a non-resident subject which\ + carries out transactions in Italy with relevance for VAT\ + purposes and which takes avail of a tax representative in\ + Italy") + l10n_it_tax_representative_partner_id = fields.Many2one('res.partner', string='Tax representative partner') + + @api.constrains('l10n_it_has_eco_index', + 'l10n_it_eco_index_office', + 'l10n_it_eco_index_number', + 'l10n_it_eco_index_share_capital', + 'l10n_it_eco_index_sole_shareholder', + 'l10n_it_eco_index_liquidation_state') + def _check_eco_admin_index(self): + for record in self: + if not record.l10n_it_has_eco_index: + continue + if not record.l10n_it_eco_index_office\ + or not record.l10n_it_eco_index_number\ + or not record.l10n_it_eco_index_share_capital\ + or not record.l10n_it_eco_index_sole_shareholder\ + or not record.l10n_it_eco_index_liquidation_state: + raise ValidationError("All fields about the Economic and Administrative Index must be completed.") + + @api.constrains('l10n_it_has_tax_representative', + 'l10n_it_tax_representative_partner_id') + def _check_tax_representative(self): + for record in self: + if not record.l10n_it_has_tax_representative: + continue + if not record.l10n_it_tax_representative_partner_id: + raise ValidationError("You must select a tax representative.") + if not record.l10n_it_tax_representative_partner_id.vat: + raise ValidationError("Your tax representative partner must have a tax number.") + if not record.l10n_it_tax_representative_partner_id.country_id: + raise ValidationError("Your tax representative partner must have a country.") diff --git a/addons/l10n_it_edi/models/res_partner.py b/addons/l10n_it_edi/models/res_partner.py new file mode 100644 index 00000000000..e0a78f24d58 --- /dev/null +++ b/addons/l10n_it_edi/models/res_partner.py @@ -0,0 +1,28 @@ +# -*- coding:utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import api, fields, models +from odoo.exceptions import ValidationError + +class ResPartner(models.Model): + _name = 'res.partner' + _inherit = 'res.partner' + + l10n_it_pec_email = fields.Char(string="PEC e-mail") + l10n_it_codice_fiscale = fields.Char(string="Codice Fiscale", size=16) + l10n_it_pa_index = fields.Char(string="PA index", + size=7, + help="Must contain the 6-character (or 7) code, present in the PA\ + Index in the information relative to the electronic invoicing service,\ + associated with the office which, within the addressee administration, deals\ + with receiving (and processing) the invoice.") + + _sql_constraints = [ + ('l10n_it_codice_fiscale', + "CHECK(l10n_it_codice_fiscale IS NULL OR LENGTH(l10n_it_codice_fiscale) >= 11)", + "Codice fiscale must have between 11 and 16 characters."), + + ('l10n_it_pa_index', + "CHECK(l10n_it_pa_index IS NULL OR LENGTH(l10n_it_pa_index) >= 6)", + "PA index must have between 6 and 7 characters."), + ] diff --git a/addons/l10n_it_edi/security/ir.model.access.csv b/addons/l10n_it_edi/security/ir.model.access.csv new file mode 100644 index 00000000000..22c0da9c159 --- /dev/null +++ b/addons/l10n_it_edi/security/ir.model.access.csv @@ -0,0 +1,2 @@ +"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" +"access_it_ddt_manager","it_ddt manager","model_l10n_it_ddt","account.group_account_invoice",1,1,1,1 \ No newline at end of file diff --git a/addons/l10n_it_edi/tests/__init__.py b/addons/l10n_it_edi/tests/__init__.py new file mode 100644 index 00000000000..d7b285d123c --- /dev/null +++ b/addons/l10n_it_edi/tests/__init__.py @@ -0,0 +1,2 @@ +# -*- coding: utf-8 -*- +from . import test_generate diff --git a/addons/l10n_it_edi/tests/test_generate.py b/addons/l10n_it_edi/tests/test_generate.py new file mode 100644 index 00000000000..60592ade8c7 --- /dev/null +++ b/addons/l10n_it_edi/tests/test_generate.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- +import time + +from odoo import fields +from odoo.tests.common import TransactionCase, Form + +class TestItalianElectronicInvoice(TransactionCase): + def test_state(self): + f = Form(self.env['account.invoice']) + f.partner_id = self.env.ref('base.res_partner_12') + with f.invoice_line_ids.new() as l: + l.product_id = self.env.ref('product.product_product_3') + invoice = f.save() + + # I check that Initially customer invoice state is "Draft" + self.assertEqual(invoice.state, 'draft') + + # I called the "Confirm Draft Invoices" wizard + w = Form(self.env['account.invoice.confirm']).save() + # I clicked on Confirm Invoices Button + w.with_context( + active_model='account.invoice', + active_id=invoice.id, + active_ids=invoice.ids, + type='out_invoice', + ).invoice_confirm() + + # I check that customer invoice state is "Open" + self.assertEqual(invoice.state, 'open') + + # Electronic invoice must be present and have the same name as l10n_it_einvoice_name + self.assertEqual(invoice.l10n_it_einvoice_id.name, invoice.l10n_it_einvoice_name) diff --git a/addons/l10n_it_edi/views/l10n_it_view.xml b/addons/l10n_it_edi/views/l10n_it_view.xml new file mode 100644 index 00000000000..7ae8222bf79 --- /dev/null +++ b/addons/l10n_it_edi/views/l10n_it_view.xml @@ -0,0 +1,265 @@ + + + + fetchmail.server.form.l10n.it + fetchmail.server + 20 + + + + + + + + + + + + account.tax.form.l10n.it + account.tax + 20 + + + + + + + + + + + + + + + + + res.partner.form.l10n.it + res.partner + 20 + + + + + + + + + + + + + + res.company.form.l10n.it + res.company + 20 + + + + + + + + + + + + + + + + +
+ The seller/provider is a company listed on the register of companies and as + such must also indicate the registration data on all documents (art. 2250, Italian + Civil Code) +
+ + + + + + + + + + +
+ + +
+ The seller/provider is a non-resident subject which carries out transactions in Italy + with relevance for VAT purposes and which takes avail of a tax representative in Italy +
+ + + + + + +
+
+
+
+
+
+ + + account.invoice.supplier.tree.l10n.it + account.invoice + 20 + + + + + + +
'); for (i = 0; i < 3 * nbrCols; i++) { diff --git a/addons/web/static/src/scss/form_view.scss b/addons/web/static/src/scss/form_view.scss index 5642e656dea..0645e00544e 100644 --- a/addons/web/static/src/scss/form_view.scss +++ b/addons/web/static/src/scss/form_view.scss @@ -744,6 +744,10 @@ .o_field_widget { width: 50%; flex: 0 0 auto; + + &.o_field_many2manytags > .o_field_widget { + flex: 1 0 50px; + } } button.btn-link:first-child { padding-left: 0; diff --git a/addons/web/static/src/scss/graph_view.scss b/addons/web/static/src/scss/graph_view.scss index 71ec5fbca9c..e67edbd3cb5 100644 --- a/addons/web/static/src/scss/graph_view.scss +++ b/addons/web/static/src/scss/graph_view.scss @@ -24,6 +24,35 @@ // ligther area color fill-opacity: 0.1; } + .nv-lineChart .nv-x.nv-axis .nv-axisMaxMin-x { + &.nv-axisMin-x text { + text-anchor: start !important; + } + &.nv-axisMax-x text { + text-anchor: end !important; + } + } + } + + /* Use less than 50% of the screen area for tooltip and show legends in 3 column of 33% each */ + &.o_tooltip_split_in_columns .nvtooltip { + max-width: 48%; + tbody { + display: flex; + flex-flow: wrap; + tr { + width: 33%; + td { + display: inline-block; + &.key { + min-width: 160px; + max-width: 70%; + overflow-x: hidden; + text-overflow: ellipsis; + } + } + } + } } } } diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml index 43d8f6900e8..8126fcf11c4 100644 --- a/addons/web/static/src/xml/base.xml +++ b/addons/web/static/src/xml/base.xml @@ -347,7 +347,7 @@ -
- % if ctx['user'].has_group('base.group_system'): + % if user.has_group('base.group_system'):
Want to customize the email? Choose the metrics you care about diff --git a/addons/digest/models/digest.py b/addons/digest/models/digest.py index 9446f5454a2..7809648e48c 100644 --- a/addons/digest/models/digest.py +++ b/addons/digest/models/digest.py @@ -101,7 +101,7 @@ class Digest(models.Model): for digest in self: for user in digest.user_ids: subject = '%s: %s' % (user.company_id.name, digest.name) - digest.template_id.with_context(user=user, company=user.company_id).send_mail(digest.id, force_send=True, raise_exception=True, email_values={'email_to': user.email, 'subject': subject}) + digest.template_id.with_context(user=user).send_mail(digest.id, force_send=True, raise_exception=True, email_values={'email_to': user.email, 'subject': subject}) digest.next_run_date = digest._get_next_run_date() def compute_kpis(self, company, user): diff --git a/addons/gamification/data/gamification_karma_rank_demo.xml b/addons/gamification/data/gamification_karma_rank_demo.xml index 45b57ae34f3..26c639a8cc9 100644 --- a/addons/gamification/data/gamification_karma_rank_demo.xml +++ b/addons/gamification/data/gamification_karma_rank_demo.xml @@ -1,5 +1,5 @@ - + 20 @@ -15,7 +15,7 @@
Reach the next rank and gain a very nice mug !
- +
100 @@ -27,7 +27,7 @@
Reach the next rank and gain a very magic wand !
- +
500 @@ -39,7 +39,7 @@
Reach the next rank and gain a very nice hat !
- +
2000 @@ -51,7 +51,7 @@
Reach the next rank and gain a week-end with a unicorn !
- +
10000 diff --git a/addons/gamification/static/img/rank_misc_hat.jpg b/addons/gamification/static/img/rank_misc_hat.jpg deleted file mode 100644 index 68abf33bc14..00000000000 Binary files a/addons/gamification/static/img/rank_misc_hat.jpg and /dev/null differ diff --git a/addons/gamification/static/img/rank_misc_hat.png b/addons/gamification/static/img/rank_misc_hat.png new file mode 100644 index 00000000000..172219569c0 Binary files /dev/null and b/addons/gamification/static/img/rank_misc_hat.png differ diff --git a/addons/gamification/static/img/rank_misc_mug.jpg b/addons/gamification/static/img/rank_misc_mug.jpg deleted file mode 100644 index 09cc5567b0e..00000000000 Binary files a/addons/gamification/static/img/rank_misc_mug.jpg and /dev/null differ diff --git a/addons/gamification/static/img/rank_misc_mug.png b/addons/gamification/static/img/rank_misc_mug.png new file mode 100644 index 00000000000..08e1fae9a1f Binary files /dev/null and b/addons/gamification/static/img/rank_misc_mug.png differ diff --git a/addons/gamification/static/img/rank_misc_unicorn.jpg b/addons/gamification/static/img/rank_misc_unicorn.jpg deleted file mode 100644 index 5612d5f7855..00000000000 Binary files a/addons/gamification/static/img/rank_misc_unicorn.jpg and /dev/null differ diff --git a/addons/gamification/static/img/rank_misc_unicorn.png b/addons/gamification/static/img/rank_misc_unicorn.png new file mode 100644 index 00000000000..c22056dcabe Binary files /dev/null and b/addons/gamification/static/img/rank_misc_unicorn.png differ diff --git a/addons/gamification/static/img/rank_misc_wand.jpg b/addons/gamification/static/img/rank_misc_wand.jpg deleted file mode 100644 index 4e2ec0457f3..00000000000 Binary files a/addons/gamification/static/img/rank_misc_wand.jpg and /dev/null differ diff --git a/addons/gamification/static/img/rank_misc_wand.png b/addons/gamification/static/img/rank_misc_wand.png new file mode 100644 index 00000000000..0101c36f8d9 Binary files /dev/null and b/addons/gamification/static/img/rank_misc_wand.png differ diff --git a/addons/google_calendar/models/google_calendar.py b/addons/google_calendar/models/google_calendar.py index ac1793f428f..9155cf9a0fa 100644 --- a/addons/google_calendar/models/google_calendar.py +++ b/addons/google_calendar/models/google_calendar.py @@ -442,7 +442,9 @@ class GoogleCalendar(models.AbstractModel): if google_attendee.get('found'): continue - attendee = ResPartner.search([('email', '=', google_attendee['email'])], limit=1) + attendee = ResPartner.search([('email', '=ilike', google_attendee['email']), ('user_ids', '!=', False)], limit=1) + if not attendee: + attendee = ResPartner.search([('email', '=ilike', google_attendee['email'])], limit=1) if not attendee: data = { 'email': partner_email, diff --git a/addons/hr/security/hr_security.xml b/addons/hr/security/hr_security.xml index 09e16244846..aa08e5f0762 100644 --- a/addons/hr/security/hr_security.xml +++ b/addons/hr/security/hr_security.xml @@ -21,6 +21,13 @@
+ + Employee multi company rule + + + ['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])] + + Department multi company rule diff --git a/addons/hr/views/hr_views.xml b/addons/hr/views/hr_views.xml index 235c4e996b8..9f71be2a9c4 100644 --- a/addons/hr/views/hr_views.xml +++ b/addons/hr/views/hr_views.xml @@ -773,6 +773,17 @@ + + res.users.preferences.form.resource + res.users + + + + 1 + + + + 0 and taxes['total_excluded'], - 'credit': taxes['total_excluded'] < 0 and -taxes['total_excluded'], - 'amount_currency': taxes['total_excluded'] > 0 and abs(amount_currency) or -abs(amount_currency), + 'debit': amount if amount > 0 else 0, + 'credit': -amount if amount < 0 else 0, + 'amount_currency': amount_currency if different_currency else 0.0, 'account_id': account_src.id, 'product_id': expense.product_id.id, 'product_uom_id': expense.product_uom_id.id, @@ -308,23 +312,25 @@ class HrExpense(models.Model): # taxes move lines for tax in taxes['taxes']: - price = expense.currency_id._convert( - tax['amount'], company_currency, expense.company_id, account_date) - amount_currency = price if different_currency else False + amount = tax['amount'] + amount_currency = False + if different_currency: + amount = expense.currency_id._convert(amount, company_currency, expense.company_id, account_date) + amount_currency = tax['amount'] move_line_tax_values = { 'name': tax['name'], 'quantity': 1, - 'debit': price > 0 and price, - 'credit': price < 0 and -price, - 'amount_currency': price > 0 and abs(amount_currency) or -abs(amount_currency), + 'debit': amount if amount > 0 else 0, + 'credit': -amount if amount < 0 else 0, + 'amount_currency': amount_currency if different_currency else 0.0, 'account_id': tax['account_id'] or move_line_src['account_id'], 'tax_line_id': tax['id'], 'expense_id': expense.id, 'partner_id': partner_id, - 'currency_id': expense.currency_id if different_currency else False, + 'currency_id': expense.currency_id.id if different_currency else False, } - total_amount -= price - total_amount_currency -= move_line_tax_values['amount_currency'] or price + total_amount -= amount + total_amount_currency -= move_line_tax_values['amount_currency'] or amount move_line_values.append(move_line_tax_values) # destination move line @@ -334,7 +340,7 @@ class HrExpense(models.Model): 'credit': total_amount < 0 and -total_amount, 'account_id': account_dst, 'date_maturity': account_date, - 'amount_currency': total_amount > 0 and abs(amount_currency) or -abs(amount_currency), + 'amount_currency': total_amount_currency if different_currency else 0.0, 'currency_id': expense.currency_id.id if different_currency else False, 'expense_id': expense.id, 'partner_id': partner_id, diff --git a/addons/hr_expense/tests/test_expenses.py b/addons/hr_expense/tests/test_expenses.py index 62eb6367365..66a56b2f557 100644 --- a/addons/hr_expense/tests/test_expenses.py +++ b/addons/hr_expense/tests/test_expenses.py @@ -78,6 +78,73 @@ class TestAccountEntry(TestExpenseCommon): self.assertAlmostEquals(self.analytic_account.line_ids[0].amount, -636.36, "Amount on the only AAL is wrong") self.assertEquals(self.analytic_account.line_ids[0].product_id, self.product_expense, "Product of AAL should be the one from the expense") + def test_account_entry_multi_currency(self): + """ Checking accounting move entries and analytic entries when submitting expense. With + multi-currency. And taxes. """ + + # Clean-up the rates + self.cr.execute("UPDATE res_company SET currency_id = %s WHERE id = %s", [self.env.ref('base.USD').id, self.env.user.company_id.id]) + self.env['res.currency.rate'].search([]).unlink() + self.env['res.currency.rate'].create({ + 'currency_id': self.env.ref('base.EUR').id, + 'company_id': self.env.user.company_id.id, + 'rate': 2.0, + 'name': '2010-01-01', + }) + + expense = self.env['hr.expense.sheet'].create({ + 'name': 'Expense for Dick Tracy', + 'employee_id': self.employee.id, + }) + expense_line = self.env['hr.expense'].create({ + 'name': 'Choucroute Saucisse', + 'employee_id': self.employee.id, + 'product_id': self.product_expense.id, + 'unit_amount': 700.00, + 'tax_ids': [(6, 0, [self.tax.id])], + 'sheet_id': expense.id, + 'analytic_account_id': self.analytic_account.id, + 'currency_id': self.env.ref('base.EUR').id, + }) + expense_line._onchange_product_id() + # State should default to draft + self.assertEquals(expense.state, 'draft', 'Expense should be created in Draft state') + # Submitted to Manager + expense.action_submit_sheet() + self.assertEquals(expense.state, 'submit', 'Expense is not in Reported state') + # Approve + expense.approve_expense_sheets() + self.assertEquals(expense.state, 'approve', 'Expense is not in Approved state') + # Create Expense Entries + expense.action_sheet_move_create() + self.assertEquals(expense.state, 'post', 'Expense is not in Waiting Payment state') + self.assertTrue(expense.account_move_id.id, 'Expense Journal Entry is not created') + + # Should get this result [(0.0, 350.0, -700.0), (318.18, 0.0, 636.36), (31.82, 0.0, 63.64)] + for line in expense.account_move_id.line_ids: + if line.credit: + self.assertAlmostEquals(line.credit, 350.0) + self.assertAlmostEquals(line.amount_currency, -700.0) + self.assertEquals(len(line.analytic_line_ids), 0, "The credit move line should not have analytic lines") + self.assertFalse(line.product_id, "Product of credit move line should be false") + else: + if not line.tax_line_id == self.tax: + self.assertAlmostEquals(line.debit, 318.18) + self.assertAlmostEquals(line.amount_currency, 636.36) + self.assertEquals(len(line.analytic_line_ids), 1, "The debit move line should have 1 analytic lines") + self.assertEquals(line.product_id, self.product_expense, "Product of debit move line should be the one from the expense") + else: + self.assertAlmostEquals(line.debit, 31.82) + self.assertAlmostEquals(line.amount_currency, 63.64) + self.assertEquals(len(line.analytic_line_ids), 0, "The tax move line should not have analytic lines") + self.assertFalse(line.product_id, "Product of tax move line should be false") + + self.assertEquals(self.analytic_account.line_ids, expense.account_move_id.mapped('line_ids.analytic_line_ids')) + self.assertEquals(len(self.analytic_account.line_ids), 1, "Analytic Account should have only one line") + self.assertAlmostEquals(self.analytic_account.line_ids[0].amount, -318.18, "Amount on the only AAL is wrong") + self.assertAlmostEquals(self.analytic_account.line_ids[0].currency_id, self.env.user.company_id.currency_id, "Currency on the only AAL is wrong") + self.assertEquals(self.analytic_account.line_ids[0].product_id, self.product_expense, "Product of AAL should be the one from the expense") + def test_expense_from_email(self): user_demo = self.env.ref('base.user_demo') self.tax.price_include = False diff --git a/addons/hr_holidays/models/hr_leave.py b/addons/hr_holidays/models/hr_leave.py index 38c0f6a4644..65760f4f6d1 100644 --- a/addons/hr_holidays/models/hr_leave.py +++ b/addons/hr_holidays/models/hr_leave.py @@ -437,7 +437,7 @@ class HolidaysRequest(models.Model): """ Returns a float equals to the timedelta between two dates given as string.""" if employee_id: employee = self.env['hr.employee'].browse(employee_id) - return employee.get_work_days_data(date_from, date_to)['days'] + return employee._get_work_days_data(date_from, date_to)['days'] today_hours = self.env.user.company_id.resource_calendar_id.get_work_hours_count( datetime.combine(date_from.date(), time.min), @@ -568,12 +568,15 @@ class HolidaysRequest(models.Model): @api.multi def _create_resource_leave(self): - """ This method will create entry in resource calendar time off object at the time of holidays validated """ + """ This method will create entry in resource calendar time off object at the time of holidays validated + :returns: created `resource.calendar.leaves` + """ + vals_list = [] for leave in self: date_from = fields.Datetime.from_string(leave.date_from) date_to = fields.Datetime.from_string(leave.date_to) - self.env['resource.calendar.leaves'].create({ + vals_list.append({ 'name': leave.name, 'date_from': fields.Datetime.to_string(date_from), 'holiday_id': leave.id, @@ -582,7 +585,7 @@ class HolidaysRequest(models.Model): 'calendar_id': leave.employee_id.resource_calendar_id.id, 'time_type': leave.holiday_status_id.time_type, }) - return True + return self.env['resource.calendar.leaves'].create(vals_list) @api.multi def _remove_resource_leave(self): @@ -634,7 +637,7 @@ class HolidaysRequest(models.Model): 'request_date_from': self.date_from, 'request_date_to': self.date_to, 'notes': self.notes, - 'number_of_days': employee.get_work_days_data(self.date_from, self.date_to)['days'], + 'number_of_days': employee._get_work_days_data(self.date_from, self.date_to)['days'], 'parent_id': self.id, 'employee_id': employee.id } diff --git a/addons/hr_holidays/models/hr_leave_allocation.py b/addons/hr_holidays/models/hr_leave_allocation.py index 87ed680ccf6..206a3933555 100644 --- a/addons/hr_holidays/models/hr_leave_allocation.py +++ b/addons/hr_holidays/models/hr_leave_allocation.py @@ -178,8 +178,8 @@ class HolidaysAllocation(models.Model): if period_start <= creation_date: period_start = creation_date - worked = holiday.employee_id.get_work_days_data(period_start, period_end, domain=[('holiday_id.holiday_status_id.unpaid', '=', True), ('time_type', '=', 'leave')])['days'] - left = holiday.employee_id.get_leave_days_data(period_start, period_end, domain=[('holiday_id.holiday_status_id.unpaid', '=', True), ('time_type', '=', 'leave')])['days'] + worked = holiday.employee_id._get_work_days_data(period_start, period_end, domain=[('holiday_id.holiday_status_id.unpaid', '=', True), ('time_type', '=', 'leave')])['days'] + left = holiday.employee_id._get_leave_days_data(period_start, period_end, domain=[('holiday_id.holiday_status_id.unpaid', '=', True), ('time_type', '=', 'leave')])['days'] prorata = worked / (left + worked) if worked else 0 days_to_give = holiday.number_per_interval diff --git a/addons/hr_holidays/models/hr_leave_type.py b/addons/hr_holidays/models/hr_leave_type.py index daa49d1e989..8d461c53a78 100644 --- a/addons/hr_holidays/models/hr_leave_type.py +++ b/addons/hr_holidays/models/hr_leave_type.py @@ -294,7 +294,10 @@ class HolidaysType(models.Model): if record.allocation_type != 'no': name = "%(name)s (%(count)s)" % { 'name': name, - 'count': _('%g remaining out of %g') % (float_round(record.virtual_remaining_leaves, precision_digits=2) or 0.0, float_round(record.max_leaves, precision_digits=2) or 0.0) + 'count': _('%g remaining out of %g') % ( + float_round(record.virtual_remaining_leaves, precision_digits=2) or 0.0, + float_round(record.max_leaves, precision_digits=2) or 0.0, + ) } res.append((record.id, name)) return res diff --git a/addons/hr_payroll/__manifest__.py b/addons/hr_payroll/__manifest__.py index e8e69c66a45..0ea4b76f672 100644 --- a/addons/hr_payroll/__manifest__.py +++ b/addons/hr_payroll/__manifest__.py @@ -30,7 +30,6 @@ 'views/report_contributionregister_templates.xml', 'views/report_payslip_templates.xml', 'views/hr_benefit_views.xml', - 'views/hr_benefit_views.xml', 'views/hr_leave_views.xml', 'views/resource_views.xml', 'views/hr_benefit_template.xml', diff --git a/addons/hr_payroll/models/hr_benefit.py b/addons/hr_payroll/models/hr_benefit.py index bafe3fd7414..6c3367fd3aa 100644 --- a/addons/hr_payroll/models/hr_benefit.py +++ b/addons/hr_payroll/models/hr_benefit.py @@ -1,14 +1,17 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -import pytz import itertools +import pytz + from psycopg2 import IntegrityError from dateutil.relativedelta import relativedelta -from odoo import api, fields, models, exceptions, _ -from odoo.tools import mute_logger -from odoo.exceptions import UserError, ValidationError + +from odoo import api, fields, models, _ from odoo.addons.resource.models.resource import Intervals +from odoo.exceptions import ValidationError +from odoo.tools import mute_logger + class HrBenefit(models.Model): _name = 'hr.benefit' @@ -17,8 +20,7 @@ class HrBenefit(models.Model): name = fields.Char(required=True) active = fields.Boolean(default=True) - employee_id = fields.Many2one('hr.employee', required=True, - domain=lambda self: [('contract_ids.state', 'in', ('open', 'pending')), ('company_id', '=', self.env.user.company_id.id)]) + employee_id = fields.Many2one('hr.employee', required=True, domain=[('contract_ids.state', 'in', ('open', 'pending'))]) date_start = fields.Datetime(required=True, string='From') date_stop = fields.Datetime(string='To') duration = fields.Float(compute='_compute_duration', inverse='_inverse_duration', store=True, string="Period") @@ -37,34 +39,25 @@ class HrBenefit(models.Model): default=lambda self: self.env['res.company']._company_default_get()) _sql_constraints = [ - ('_unique', 'unique (employee_id, date_start, date_stop, benefit_type_id)', "Benefit already exists for this attendence"), + ('_unique', 'unique (date_start, date_stop, employee_id, benefit_type_id)', "Benefit already exists for this attendance"), + ('_benefit_has_end', 'check (date_stop IS NOT NULL OR duration <> 0)', 'Benefit must end. Please define an end date or a duration.'), + ('_benefit_start_before_end', 'check (date_stop is null OR (date_stop > date_start))', 'Starting time should be before end time.') ] - @api.constrains('date_stop', 'duration') - def _check_validity_benefit_ends(self): - """ verifies if benefit has an end. """ - for benefit in self: - if not (benefit.date_stop or benefit.duration): - raise exceptions.ValidationError(_('Benefit must end. Please define an end date or a duration.')) - - @api.constrains('date_start', 'date_stop') - def _check_validity_start_before_end(self): - """ verifies if benefit has an end. """ - for benefit in self: - if benefit.date_stop < benefit.date_start: - raise exceptions.ValidationError(_('Starting time should be before end time.')) - - @api.onchange('duration') def _onchange_duration(self): self._inverse_duration() + def _get_duration(self, date_start, date_stop): + if not date_start or not date_stop: + return 0 + dt = date_stop - date_start + return dt.days * 24 + dt.seconds / 3600 # Number of hours + @api.depends('date_stop', 'date_start') def _compute_duration(self): for benefit in self: - if benefit.date_start and benefit.date_stop: - dt = benefit.date_stop - benefit.date_start - benefit.duration = dt.days * 24 + dt.seconds / 3600 # Number of hours + benefit.duration = benefit._get_duration(benefit.date_start, benefit.date_stop) def _inverse_duration(self): for benefit in self: @@ -81,25 +74,22 @@ class HrBenefit(models.Model): return super(HrBenefit, self).write(vals) @api.multi - def check_if_error(self): + def _check_if_error(self): if not self: return False undefined_type = self.filtered(lambda b: not b.benefit_type_id) undefined_type.write({'display_warning': True}) - conflict = self._compute_schedule_conflicts() - conflict_with_leaves = self.compute_conflicts_leaves_to_approve() + conflict = self._mark_conflicting_benefits(min(self.mapped('date_start')), max(self.mapped('date_stop'))) + conflict_with_leaves = self._compute_conflicts_leaves_to_approve() return undefined_type or conflict or conflict_with_leaves - @api.multi - def _compute_schedule_conflicts(self): + @api.model + def _mark_conflicting_benefits(self, start, stop): conflict = False - date_start_benefits = min(self.mapped('date_start')) - date_stop_benefits = max(self.mapped('date_stop')) domain = [ - ('date_start', '<', date_stop_benefits), - ('date_stop', '>', date_start_benefits), + ('date_start', '<', stop), + ('date_stop', '>', start), ] - benefs = self.search(domain) benefits_by_employee = itertools.groupby(benefs, lambda b: b.employee_id) for employee, benefs in benefits_by_employee: @@ -111,8 +101,7 @@ class HrBenefit(models.Model): return conflict @api.multi - def compute_conflicts_leaves_to_approve(self): - + def _compute_conflicts_leaves_to_approve(self): if not self: return False @@ -137,26 +126,35 @@ class HrBenefit(models.Model): }) return bool(conflicts) - def safe_duplicate_create(self, vals): + def _safe_duplicate_create(self, vals_list, date_start, date_stop): """ - Create benefit but silently abord if it already exists in the database (according to the _unique constraints). - @return: new record if it didn't exist, empty recordset otherwise. + Create benefits between date_start and date_stop according to vals_list. + Skip the values in vals_list if a benefit already exists for the given + date_start, date_stop, employee_id, benefit_type_id + :return: new record id if it didn't exist. """ - try: - with mute_logger('odoo.sql_db'), self.env.cr.savepoint(): - # Any database call will be run inside a postgresql savepoint which is - # rolledback if an exception occurs allowing to make subsequent calls in the transaction. - # (Otherwise InternalError is raised by any subsequent database operations) - return self.create(vals) - except IntegrityError as err: - if 'hr_benefit__unique' not in err.pgerror: - raise err - return self.env['hr.benefit'] + # The search_read should be fast as date_start and date_stop are indexed from the + # unique sql constraint + month_recs = self.search_read([('date_start', '>=', date_start), ('date_stop', '<=', date_stop)], + ['employee_id', 'date_start', 'date_stop', 'benefit_type_id']) + existing_entries = {( + r['date_start'], + r['date_stop'], + 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] + # Remove duplicates from vals_list, shouldn't be necessary from saas-12.2 + unique_new_vals = set() + for values in new_vals: + unique_new_vals.add(tuple(values.items())) + new_vals = [dict(values) for values in unique_new_vals] + return self.create(new_vals) def action_leave(self): leave = self.leave_id return { - 'type':'ir.actions.act_window', + 'type': 'ir.actions.act_window', 'view_type': 'form', 'view_mode': 'form', 'res_id': leave.id, @@ -164,7 +162,7 @@ class HrBenefit(models.Model): 'views': [[False, 'form']], } - def split_by_day(self): + def _split_by_day(self): """ Split the benefit by days and unlink the original benefit. @return recordset @@ -184,6 +182,8 @@ class HrBenefit(models.Model): return [(start, end) for start, end in days if start != end] new_benefits = self.env['hr.benefit'] + benefits_to_unlink = self.env['hr.benefit'] + for benefit in self: if benefit.date_start.date() == benefit.date_stop.date(): new_benefits |= benefit @@ -197,7 +197,7 @@ class HrBenefit(models.Model): 'contract_id': benefit.contract_id.id, } benefit_state = benefit.state - benefit.unlink() + 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) @@ -206,6 +206,7 @@ class HrBenefit(models.Model): new_benefit.state = benefit_state new_benefits |= new_benefit + benefits_to_unlink.unlink() return new_benefits @api.multi @@ -213,31 +214,36 @@ class HrBenefit(models.Model): """ Duplicate data to keep the complexity in benefit and not mess up payroll, etc. """ + attendance_type = self.env.ref('hr_payroll.benefit_type_attendance') attendance_benefits = self.filtered(lambda b: not b.benefit_type_id.is_leave and # Normal benefit are global to all employees -> avoid duplicating it - not b.benefit_type_id == self.env.ref('hr_payroll.benefit_type_attendance')) + not b.benefit_type_id == attendance_type) leave_benefits = self.filtered(lambda b: b.benefit_type_id.is_leave) + benefits_to_duplicate = self.env['hr.benefit'] for benefit in attendance_benefits: - benefit = benefit.split_by_day() - benefit._duplicate_to_calendar_attendance() + benefit = benefit._split_by_day() + benefits_to_duplicate |= benefit + + benefits_to_duplicate._duplicate_to_calendar_attendance() leave_benefits._duplicate_to_calendar_leave() @api.multi def _duplicate_to_calendar_leave(self): - + vals_list = [] for benefit in self: if not benefit.leave_id: - tz = pytz.timezone(benefit.employee_id.tz) - self.env['resource.calendar.leaves'].create({ + vals_list += [{ 'name': benefit.name, 'date_from': benefit.date_start, 'date_to': benefit.date_stop, 'calendar_id': benefit.employee_id.resource_calendar_id.id, 'resource_id': benefit.employee_id.resource_id.id, 'benefit_type_id': benefit.benefit_type_id.id, - }) + }] + if vals_list: + self.env['resource.calendar.leaves'].create(vals_list) @api.multi def _duplicate_to_calendar_attendance(self): @@ -251,32 +257,35 @@ class HrBenefit(models.Model): if any(data[0].date() != data[1].date() for data in mapped_data.values()): raise ValidationError(_("You can't validate a benefit that covers several days.")) + vals_list = [] for benefit in self: start, end = mapped_data.get(benefit) - self.env['resource.calendar.attendance'].create({ + vals_list += [{ 'name': benefit.name, 'dayofweek': str(start.weekday()), 'date_from': start.date(), 'date_to': end.date(), - 'hour_from':start.hour + start.minute/60, - 'hour_to': end.hour + end.minute/60, + 'hour_from': start.hour + start.minute / 60, + 'hour_to': end.hour + end.minute / 60, 'calendar_id': benefit.contract_id.resource_calendar_id.id, 'day_period': 'morning' if end.hour <= 12 else 'afternoon', 'resource_id': benefit.employee_id.resource_id.id, 'benefit_type_id': benefit.benefit_type_id.id, - }) + }] + self.env['resource.calendar.attendance'].create(vals_list) @api.model def action_validate(self, ids): benefits = self.env['hr.benefit'].search([('id', 'in', ids), ('state', '!=', 'validated')]) benefits.write({'display_warning': False}) - if not benefits.check_if_error(): + if not benefits._check_if_error(): benefits.write({'state': 'validated'}) benefits._duplicate_to_calendar() return True return False + class HrBenefitType(models.Model): _name = 'hr.benefit.type' _description = 'hr.benefit.type' @@ -285,8 +294,9 @@ class HrBenefitType(models.Model): code = fields.Char() color = fields.Integer(default=1) # Will be used with the new calendar/kanban view sequence = fields.Integer(default=25) - active = fields.Boolean('Active', default=True, - help="If the active field is set to false, it will allow you to hide the benefit type without removing it.") + active = fields.Boolean( + 'Active', default=True, + help="If the active field is set to false, it will allow you to hide the benefit type without removing it.") is_leave = fields.Boolean(default=False, string="Leave") class Contacts(models.Model): @@ -301,4 +311,4 @@ class Contacts(models.Model): _sql_constraints = [ ('user_id_employee_id_unique', 'UNIQUE(user_id,employee_id)', 'You cannot have twice the same employee.') - ] + ] \ No newline at end of file diff --git a/addons/hr_payroll/models/hr_employee.py b/addons/hr_payroll/models/hr_employee.py index ec3d6df3198..527f70c41ad 100644 --- a/addons/hr_payroll/models/hr_employee.py +++ b/addons/hr_payroll/models/hr_employee.py @@ -44,12 +44,15 @@ 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 + 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) @@ -70,9 +73,10 @@ class HrEmployee(models.Model): r.date_to.replace(tzinfo=pytz.utc) >= date_start ) global_leaves = employee.resource_calendar_id.global_leave_ids - (emp_leaves | global_leaves).mapped('holiday_id').copy_to_benefits() - new_benefits = self.env['hr.benefit'] + employee_leaves = (emp_leaves | global_leaves).mapped('holiday_id') + 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)) @@ -83,8 +87,8 @@ class HrEmployee(models.Model): attendances = calendar._work_intervals(date_start_benefits, date_stop_benefits, resource=resource) # Attendances for interval in attendances: - benefit_type_id = interval[2].mapped('benefit_type_id')[:1] or self.env.ref('hr_payroll.benefit_type_attendance') - new_benefits |= self.env['hr.benefit'].safe_duplicate_create({ + benefit_type_id = interval[2].mapped('benefit_type_id')[:1] or attendance_type + 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), @@ -92,6 +96,7 @@ class HrEmployee(models.Model): 'employee_id': employee.id, 'contract_id': contract.id, 'state': 'confirmed', - }) + }] - new_benefits.compute_conflicts_leaves_to_approve() + new_benefits = self.env['hr.benefit']._safe_duplicate_create(vals_list, date_start, date_stop) + new_benefits._compute_conflicts_leaves_to_approve() diff --git a/addons/hr_payroll/models/hr_leave.py b/addons/hr_payroll/models/hr_leave.py index c4cee2cf791..e69aaa8b85f 100644 --- a/addons/hr_payroll/models/hr_leave.py +++ b/addons/hr_payroll/models/hr_leave.py @@ -18,13 +18,14 @@ class HrLeave(models.Model): _inherit = 'hr.leave' @api.multi - def copy_to_benefits(self): + def _get_benefits_values(self): + vals_list = [] for leave in self: contract = leave.employee_id._get_contracts(leave.date_from, leave.date_to, states=['open', 'pending', 'close']) start = max(leave.date_from, datetime.combine(contract.date_start, datetime.min.time())) end = min(leave.date_to, datetime.combine(contract.date_end or date.max, datetime.max.time())) benefit_type = leave.holiday_status_id.benefit_type_id - self.env['hr.benefit'].safe_duplicate_create({ + vals_list += [{ 'name': "%s%s" % (benefit_type.name + ": " if benefit_type else "", leave.employee_id.name), 'date_start': start, 'date_stop': end, @@ -34,7 +35,8 @@ class HrLeave(models.Model): 'leave_id': leave.id, 'state': 'confirmed', 'contract_id': contract.id, - }) + }] + return vals_list @api.multi def _create_resource_leave(self): @@ -43,7 +45,10 @@ class HrLeave(models.Model): This is needed in order to compute the correct number of hours/days of the leave according to the contract's calender. """ - super(HrLeave, self)._create_resource_leave() + resource_leaves = super(HrLeave, self)._create_resource_leave() + for resource_leave in resource_leaves: + resource_leave.benefit_type_id = resource_leave.holiday_id.holiday_status_id.benefit_type_id.id + resource_leave_values = [] for leave in self.filtered(lambda l: l.employee_id): @@ -61,8 +66,7 @@ class HrLeave(models.Model): 'calendar_id': contract.resource_calendar_id.id, }] - self.env['resource.calendar.leaves'].create(resource_leave_values) - return True + return resource_leaves | self.env['resource.calendar.leaves'].create(resource_leave_values) @api.constrains('date_from', 'date_to') def _check_contracts(self): @@ -101,7 +105,7 @@ class HrLeave(models.Model): """ benefits = self.env['hr.benefit'].search([('leave_id', 'in', self.ids)]) if benefits: - self.copy_to_benefits() + vals_list = self._get_benefits_values() # create new benefits where the leave does not cover the full benefit benefits_intervals = Intervals(intervals=[(b.date_start, b.date_stop, b) for b in benefits]) leave_intervals = Intervals(intervals=[(l.date_from, l.date_to, l) for l in self]) @@ -116,7 +120,7 @@ class HrLeave(models.Model): benefit_start = interval[0] + relativedelta(seconds=1) if leave.date_to == interval[0] else interval[0] benefit_stop = interval[1] - relativedelta(seconds=1) if leave.date_from == interval[1] else interval[1] - self.env['hr.benefit'].safe_duplicate_create({ + vals_list += [{ 'name': "%s: %s" % (benefit_type.name, employee.name), 'date_start': benefit_start, 'date_stop': benefit_stop, @@ -124,7 +128,11 @@ class HrLeave(models.Model): 'contract_id': benefit.contract_id.id, 'employee_id': employee.id, 'state': 'confirmed', - }) + }] + + date_start = min(benefits.mapped('date_start')) + date_stop = max(benefits.mapped('date_stop')) + self.env['hr.benefit']._safe_duplicate_create(vals_list, date_start, date_stop) benefits.unlink() return True return False @@ -132,13 +140,13 @@ class HrLeave(models.Model): @api.multi def action_validate(self): super(HrLeave, self).action_validate() - self._cancel_benefit_conflict() # delete preexisting conflicting benefits + self.sudo()._cancel_benefit_conflict() # delete preexisting conflicting benefits return True @api.multi def action_refuse(self): super(HrLeave, self).action_refuse() - benefits = self.env['hr.benefit'].search([('leave_id', 'in', self.ids)]) + benefits = self.env['hr.benefit'].sudo().search([('leave_id', 'in', self.ids)]) benefits.write({'display_warning': False, 'active': False}) return True @@ -155,7 +163,7 @@ class HrLeave(models.Model): # Use sudo otherwise base users can't compute number of days contracts = employee.sudo()._get_contracts(date_from, date_to, states=['incoming', 'open', 'pending']) calendar = contracts[:1].resource_calendar_id if contracts else None # Note: if len(contracts)>1, the leave creation will crash because of unicity constaint - return employee.get_work_days_data(date_from, date_to, calendar=calendar)['days'] + return employee._get_work_days_data(date_from, date_to, calendar=calendar)['days'] return days diff --git a/addons/hr_payroll/models/hr_payslip.py b/addons/hr_payroll/models/hr_payslip.py index c3f625e618c..3277d473f47 100644 --- a/addons/hr_payroll/models/hr_payslip.py +++ b/addons/hr_payroll/models/hr_payslip.py @@ -163,7 +163,7 @@ class HrPayslip(models.Model): benefit_types = self.env['hr.benefit.type'].search([('code', '!=', False)]) for benefit_type in benefit_types: - hours = contract.employee_id.get_benefit_days_data(benefit_type, day_from, day_to, calendar=calendar)['hours'] + hours = contract.employee_id._get_benefit_days_data(benefit_type, day_from, day_to, calendar=calendar)['hours'] if hours: line = { 'name': benefit_type.name, diff --git a/addons/hr_payroll/models/resource.py b/addons/hr_payroll/models/resource.py index 4763312c4f5..1dd5e0677a5 100644 --- a/addons/hr_payroll/models/resource.py +++ b/addons/hr_payroll/models/resource.py @@ -1,10 +1,11 @@ # -*- coding:utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -from odoo import models, fields, api from odoo.osv.expression import AND from odoo.tools import float_compare from datetime import datetime +from odoo import models, fields, api +from odoo.addons.resource.models.resource_mixin import timezone_datetime class ResourceCalendar(models.Model): _inherit = 'resource.calendar' @@ -78,7 +79,7 @@ class ResourceCalendarLeave(models.Model): class ResourceMixin(models.AbstractModel): _inherit = "resource.mixin" - def get_benefit_days_data(self, benefit_type, from_datetime, to_datetime, calendar=None): + def _get_benefit_days_data(self, benefit_type, from_datetime, to_datetime, calendar=None): """ By default the resource calendar is used, but it can be changed using the `calendar` argument. @@ -94,8 +95,8 @@ class ResourceMixin(models.AbstractModel): domain = [('benefit_type_id', 'in', benefit_type_ids)] # naive datetimes are made explicit in UTC - from_datetime = self._timezone_datetime(from_datetime) - to_datetime = self._timezone_datetime(to_datetime) + from_datetime = timezone_datetime(from_datetime) + to_datetime = timezone_datetime(to_datetime) day_total = self._get_day_total(from_datetime, to_datetime, calendar, resource) # actual hours per day diff --git a/addons/hr_payroll/security/ir.model.access.csv b/addons/hr_payroll/security/ir.model.access.csv index 751a64b4779..68d8dd8f6d7 100644 --- a/addons/hr_payroll/security/ir.model.access.csv +++ b/addons/hr_payroll/security/ir.model.access.csv @@ -10,5 +10,6 @@ access_hr_payslip_worked_days_officer,hr.payslip.worked_days.officer,model_hr_pa access_hr_payslip_run,hr.payslip.run,model_hr_payslip_run,hr_payroll.group_hr_payroll_manager,1,1,1,1 access_hr_salary_rule_user,hr.salary.rule.user,model_hr_salary_rule,hr_payroll.group_hr_payroll_user,1,1,1,1 access_hr_benefit,access_hr_benefit,model_hr_benefit,group_hr_payroll_user,1,1,1,1 -access_hr_benefit_type,access_hr_benefit_type,model_hr_benefit_type,group_hr_payroll_user,1,1,1,1 +access_hr_benefit_type_officer,access_hr_benefit_type_officer,model_hr_benefit_type,group_hr_payroll_user,1,0,0,0 access_hr_benefit_employee,hr.benefit.employee,model_hr_user_benefit_employee,group_hr_payroll_user,1,1,1,1 +access_hr_benefit_type_manager,access_hr_benefit_type_manager,model_hr_benefit_type,group_hr_payroll_manager,1,1,1,1 diff --git a/addons/hr_payroll/static/src/js/benefit.js b/addons/hr_payroll/static/src/js/benefit.js index f403e718a64..f021d29d63f 100644 --- a/addons/hr_payroll/static/src/js/benefit.js +++ b/addons/hr_payroll/static/src/js/benefit.js @@ -3,11 +3,11 @@ odoo.define('hr_payroll.benefit.view_custo', function(require) { var core = require('web.core'); var CalendarController = require("web.CalendarController"); + var time = require('web.time'); var CalendarModel = require('web.CalendarModel'); var CalendarRenderer = require('web.CalendarRenderer'); var CalendarView = require('web.CalendarView'); var viewRegistry = require('web.view_registry'); - var _t = core._t; var BenefitCalendarController = CalendarController.extend({ @@ -35,8 +35,8 @@ odoo.define('hr_payroll.benefit.view_custo', function(require) { return; } - this.firstDay = this.model.data.target_date.clone().startOf('month'); - this.lastDay = this.model.data.target_date.clone().endOf('month'); + this.firstDay = this.model.data.target_date.clone().startOf('month').toDate(); + this.lastDay = this.model.data.target_date.clone().endOf('month').toDate(); this.events = this._checkDataInRange(this.firstDay, this.lastDay, this.model.data.data); var is_validated = this._checkValidation(this.events); this.$buttons.find('.btn-benefit').remove(); @@ -54,11 +54,10 @@ odoo.define('hr_payroll.benefit.view_custo', function(require) { _onGeneratePayslips: function (e) { e.preventDefault(); e.stopImmediatePropagation(); - var date_fmt = 'YYYY-MM-DD'; this.do_action('hr_payroll.action_generate_payslips_from_benefits', { additional_context: { - default_date_start: this.firstDay.format(date_fmt), - default_date_end: this.lastDay.format(date_fmt), + default_date_start: time.datetime_to_str(this.firstDay), + default_date_end: time.datetime_to_str(this.lastDay), }, }); }, @@ -79,12 +78,11 @@ odoo.define('hr_payroll.benefit.view_custo', function(require) { _onGenerateBenefits: function (e) { e.preventDefault(); e.stopImmediatePropagation(); - var date_fmt = 'YYYY-MM-DD HH:mm:ss'; var self = this; this._rpc({ model: 'hr.employee', method: 'generate_benefit', - args: [this.firstDay.format(date_fmt), this.lastDay.format(date_fmt)], + args: [time.datetime_to_str(this.firstDay), time.datetime_to_str(this.lastDay)], }).then(function () { self.reload(); }); diff --git a/addons/hr_payroll/tests/__init__.py b/addons/hr_payroll/tests/__init__.py index 98f0f93e77b..bd5daca4153 100644 --- a/addons/hr_payroll/tests/__init__.py +++ b/addons/hr_payroll/tests/__init__.py @@ -5,3 +5,4 @@ from . import test_payslip_flow from . import test_benefit from . import test_multi_contract from . import test_calendar_sync +from . import test_leave diff --git a/addons/hr_payroll/tests/test_benefit.py b/addons/hr_payroll/tests/test_benefit.py index 92a5d1daa6b..083a9c287ac 100644 --- a/addons/hr_payroll/tests/test_benefit.py +++ b/addons/hr_payroll/tests/test_benefit.py @@ -5,6 +5,7 @@ from datetime import datetime import pytz from dateutil.relativedelta import relativedelta from odoo import exceptions +from odoo.fields import Datetime, Date from odoo.tests.common import tagged from odoo.addons.hr_payroll.tests.common import TestPayslipBase @@ -15,8 +16,8 @@ class TestBenefit(TestPayslipBase): def setUp(self): super(TestBenefit, self).setUp() self.tz = pytz.timezone(self.richard_emp.tz) - self.start = datetime.strptime('2015-11-01 01:00:00', '%Y-%m-%d %H:%M:%S').replace(tzinfo=self.tz) - self.end = datetime.strptime('2015-11-30 23:59:59', '%Y-%m-%d %H:%M:%S').replace(tzinfo=self.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.resource_calendar_id = self.env['resource.calendar'].create({'name': 'Zboub'}) contract = self.env['hr.contract'].create({ 'date_start': self.start - relativedelta(days=5), @@ -35,6 +36,24 @@ 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([]) @@ -51,8 +70,8 @@ class TestBenefit(TestPayslipBase): self.assertEqual(attendance_nb, benefit_nb, "One benefit should be generated for each calendar attendance") def test_split_benefit_by_day(self): - start = datetime.strptime('2015-11-01 09:00:00', '%Y-%m-%d %H:%M:%S').replace(tzinfo=self.tz) - end = datetime.strptime('2015-11-03 18:00:00', '%Y-%m-%d %H:%M:%S').replace(tzinfo=self.tz) + start = self.to_datetime_tz('2015-11-01 09:00:00') + end = self.to_datetime_tz('2015-11-03 18:00:00') # Benefit of type attendance should be split in three benefit = self.env['hr.benefit'].create({ @@ -63,20 +82,21 @@ class TestBenefit(TestPayslipBase): 'date_stop': end, }) - benefits = benefit.split_by_day() + benefits = benefit._split_by_day() self.assertEqual(len(benefits), 3, "Benefit should be split in three") - self.assertEqual(pytz.utc.localize(benefits[0].date_start), self.tz.localize(datetime.strptime('2015-11-01 09:00:00', '%Y-%m-%d %H:%M:%S'))) - self.assertEqual(pytz.utc.localize(benefits[0].date_stop), self.tz.localize(datetime.strptime('2015-11-01 23:59:59', '%Y-%m-%d %H:%M:%S'))) - self.assertEqual(pytz.utc.localize(benefits[1].date_start), self.tz.localize(datetime.strptime('2015-11-02 00:00:00', '%Y-%m-%d %H:%M:%S'))) - self.assertEqual(pytz.utc.localize(benefits[1].date_stop), self.tz.localize(datetime.strptime('2015-11-02 23:59:59', '%Y-%m-%d %H:%M:%S'))) + 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(pytz.utc.localize(benefits[2].date_start), self.tz.localize(datetime.strptime('2015-11-03 00:00:00', '%Y-%m-%d %H:%M:%S'))) - self.assertEqual(pytz.utc.localize(benefits[2].date_stop), self.tz.localize(datetime.strptime('2015-11-03 18:00:00', '%Y-%m-%d %H:%M:%S'))) + 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.assertDatetimeTzEqual(benefits[2].date_start, '2015-11-03 00:00:00') + self.assertDatetimeTzEqual(benefits[2].date_stop, '2015-11-03 18:00:00') # Test with end at mid-night -> should not create benefit starting and ending at the same time (at 00:00) - start = datetime.strptime('2013-11-01 00:00:00', '%Y-%m-%d %H:%M:%S').replace(tzinfo=self.tz) - end = datetime.strptime('2013-11-04 00:00:00', '%Y-%m-%d %H:%M:%S').replace(tzinfo=self.tz) + start = self.to_datetime_tz('2013-11-01 00:00:00') + end = self.to_datetime_tz('2013-11-04 00:00:00') benefit = self.env['hr.benefit'].create({ 'name': '1', @@ -85,21 +105,21 @@ class TestBenefit(TestPayslipBase): 'date_start': start, 'date_stop': end, }) - benefits = benefit.split_by_day() + benefits = benefit._split_by_day() self.assertEqual(len(benefits), 3, "Benefit should be split in three") - self.assertEqual(pytz.utc.localize(benefits[0].date_start), self.tz.localize(datetime.strptime('2013-11-01 00:00:00', '%Y-%m-%d %H:%M:%S'))) - self.assertEqual(pytz.utc.localize(benefits[0].date_stop), self.tz.localize(datetime.strptime('2013-11-01 23:59:59', '%Y-%m-%d %H:%M:%S'))) + 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(pytz.utc.localize(benefits[1].date_start), self.tz.localize(datetime.strptime('2013-11-02 00:00:00', '%Y-%m-%d %H:%M:%S'))) - self.assertEqual(pytz.utc.localize(benefits[1].date_stop), self.tz.localize(datetime.strptime('2013-11-02 23:59:59', '%Y-%m-%d %H:%M:%S'))) + 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(pytz.utc.localize(benefits[2].date_start), self.tz.localize(datetime.strptime('2013-11-03 00:00:00', '%Y-%m-%d %H:%M:%S'))) - self.assertEqual(pytz.utc.localize(benefits[2].date_stop), self.tz.localize(datetime.strptime('2013-11-03 23:59:59', '%Y-%m-%d %H:%M:%S'))) + self.assertDatetimeTzEqual(benefits[2].date_start, '2013-11-03 00:00:00') + self.assertDatetimeTzEqual(benefits[2].date_stop, '2013-11-03 23:59:59') def test_approve_multiple_day_benefit(self): - start = datetime.strptime('2015-11-01 09:00:00', '%Y-%m-%d %H:%M:%S').replace(tzinfo=self.tz) - end = datetime.strptime('2015-11-03 18:00:00', '%Y-%m-%d %H:%M:%S').replace(tzinfo=self.tz) + start = self.to_datetime_tz('2015-11-01 09:00:00') + end = self.to_datetime_tz('2015-11-03 18:00:00') # Benefit of type attendance should be split in three benefit = self.env['hr.benefit'].create({ @@ -116,8 +136,8 @@ class TestBenefit(TestPayslipBase): self.assertEqual(len(benefits), 3, "Benefit should be split in three") def test_duplicate_global_benefit_to_attendance(self): - start = datetime.strptime('2015-11-01 09:00:00', '%Y-%m-%d %H:%M:%S').replace(tzinfo=self.tz) - end = datetime.strptime('2015-11-03 18:00:00', '%Y-%m-%d %H:%M:%S').replace(tzinfo=self.tz) + start = self.to_datetime_tz('2015-11-01 09:00:00') + end = self.to_datetime_tz('2015-11-03 18:00:00') benef = self.env['hr.benefit'].create({ 'name': '1', @@ -135,8 +155,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 = datetime.strptime('2015-11-01 09:00:00', '%Y-%m-%d %H:%M:%S').replace(tzinfo=self.tz) - end = datetime.strptime('2015-11-03 18:00:00', '%Y-%m-%d %H:%M:%S').replace(tzinfo=self.tz) + start = self.to_datetime_tz('2015-11-01 09:00:00') + end = self.to_datetime_tz('2015-11-03 18:00:00') # Benefit (not leave) should be split in three attendance benef = self.env['hr.benefit'].create({ @@ -154,27 +174,27 @@ class TestBenefit(TestPayslipBase): ]) self.assertEqual(attendance_nb, 3, "It should create one calendar attendance per day") self.assertTrue(self.env['resource.calendar.attendance'].search([ - ('date_from', '=', datetime.strptime('2015-11-01', '%Y-%m-%d').date()), - ('date_to', '=', datetime.strptime('2015-11-01', '%Y-%m-%d').date()), + ('date_from', '=', Date.to_date('2015-11-01')), + ('date_to', '=', Date.to_date('2015-11-01')), ('hour_from', '=', 9.0), ('hour_to', '>=', 23.9) ])) self.assertTrue(self.env['resource.calendar.attendance'].search([ - ('date_from', '=', datetime.strptime('2015-11-02', '%Y-%m-%d').date()), - ('date_to', '=', datetime.strptime('2015-11-02', '%Y-%m-%d').date()), + ('date_from', '=', Date.to_date('2015-11-02')), + ('date_to', '=', Date.to_date('2015-11-02')), ('hour_from', '=', 0.0), ('hour_to', '>=', 23.9) ])) self.assertTrue(self.env['resource.calendar.attendance'].search([ - ('date_from', '=', datetime.strptime('2015-11-03', '%Y-%m-%d').date()), - ('date_to', '=', datetime.strptime('2015-11-03', '%Y-%m-%d').date()), + ('date_from', '=', Date.to_date('2015-11-03')), + ('date_to', '=', Date.to_date('2015-11-03')), ('hour_from', '=', 0.0), ('hour_to', '=', 18.0) ])) def test_create_benefit_leave(self): - start = datetime.strptime('2015-11-01 09:00:00', '%Y-%m-%d %H:%M:%S').replace(tzinfo=self.tz) - end = datetime.strptime('2015-11-03 18:00:00', '%Y-%m-%d %H:%M:%S').replace(tzinfo=self.tz) + start = self.to_datetime_tz('2015-11-01 09:00:00') + end = self.to_datetime_tz('2015-11-03 18:00:00') benef = self.env['hr.benefit'].create({ 'name': 'Richard leave from benef', @@ -190,8 +210,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 = datetime.strptime('2015-11-01 09:00:00', '%Y-%m-%d %H:%M:%S').replace(tzinfo=self.tz) - end = datetime.strptime('2015-11-01 13:00:00', '%Y-%m-%d %H:%M:%S').replace(tzinfo=self.tz) + start = self.to_datetime_tz('2015-11-01 09:00:00') + end = self.to_datetime_tz('2015-11-01 13:00:00') benef1 = self.env['hr.benefit'].create({ 'name': '1', 'employee_id': self.richard_emp.id, @@ -243,8 +263,8 @@ class TestBenefit(TestPayslipBase): self.assertFalse(benef1.action_validate(benef1.ids),"It should not validate benefits without a type") def test_approve_leave_benefit(self): - start = datetime.strptime('2015-11-01 09:00:00', '%Y-%m-%d %H:%M:%S').replace(tzinfo=self.tz) - end = datetime.strptime('2015-11-03 13:00:00', '%Y-%m-%d %H:%M:%S').replace(tzinfo=self.tz) + start = self.to_datetime_tz('2015-11-01 09:00:00') + end = self.to_datetime_tz('2015-11-03 13:00:00') leave = self.env['hr.leave'].create({ 'name': 'Doctor Appointment', 'employee_id': self.richard_emp.id, @@ -265,13 +285,13 @@ class TestBenefit(TestPayslipBase): leave.action_approve() new_leave_benef = self.env['hr.benefit'].search([ - ('date_start', '=', datetime.strptime('2015-11-01 09:00:00', '%Y-%m-%d %H:%M:%S')), - ('date_stop', '=', datetime.strptime('2015-11-02 09:00:00', '%Y-%m-%d %H:%M:%S')), + ('date_start', '=', Datetime.to_datetime('2015-11-01 09:00:00')), + ('date_stop', '=', Datetime.to_datetime('2015-11-02 09:00:00')), ('benefit_type_id.is_leave', '=', True) ]) new_benef = self.env['hr.benefit'].search([ - ('date_start', '=', datetime.strptime('2015-11-02 09:00:01', '%Y-%m-%d %H:%M:%S')), + ('date_start', '=', Datetime.to_datetime('2015-11-02 09:00:01')), ('date_stop', '=', end), ('benefit_type_id.is_leave', '=', False) ]) @@ -279,11 +299,11 @@ class TestBenefit(TestPayslipBase): self.assertTrue(new_benef, "It should have created a benefit for the last two days") self.assertTrue(new_leave_benef, "It should have created a leave benefit for the first day") - self.assertTrue(benef.action_validate((new_benef|new_leave_benef).ids), "It should be able to validate the benefits") + self.assertTrue(benef.action_validate((new_benef | new_leave_benef).ids), "It should be able to validate the benefits") def test_refuse_leave_benefit(self): - start = datetime.strptime('2015-11-01 09:00:00', '%Y-%m-%d %H:%M:%S').replace(tzinfo=self.tz) - end = datetime.strptime('2015-11-03 13:00:00', '%Y-%m-%d %H:%M:%S').replace(tzinfo=self.tz) + start = self.to_datetime_tz('2015-11-01 09:00:00') + end = self.to_datetime_tz('2015-11-03 13:00:00') leave = self.env['hr.leave'].create({ 'name': 'Doctor Appointment', 'employee_id': self.richard_emp.id, @@ -308,12 +328,12 @@ class TestBenefit(TestPayslipBase): def test_time_normal_benefit(self): # Normal attendances (global to all employees) - data = self.richard_emp.get_benefit_days_data(self.env.ref('hr_payroll.benefit_type_attendance'), self.start, self.end) + data = self.richard_emp._get_benefit_days_data(self.env.ref('hr_payroll.benefit_type_attendance'), self.start, self.end) self.assertEqual(data['hours'], 168.0) def test_time_extra_benefit(self): - start = datetime.strptime('2015-11-01 10:00:00', '%Y-%m-%d %H:%M:%S').replace(tzinfo=self.tz) - end = datetime.strptime('2015-11-01 17:00:00', '%Y-%m-%d %H:%M:%S').replace(tzinfo=self.tz) + start = self.to_datetime_tz('2015-11-01 10:00:00') + end = self.to_datetime_tz('2015-11-01 17:00:00') benef = self.env['hr.benefit'].create({ 'name': '1', 'employee_id': self.richard_emp.id, @@ -323,13 +343,13 @@ class TestBenefit(TestPayslipBase): 'date_stop': end, }) benef.action_validate(benef.ids) - data = self.richard_emp.get_benefit_days_data(self.benefit_type, self.start, self.end) + data = self.richard_emp._get_benefit_days_data(self.benefit_type, self.start, self.end) self.assertEqual(data['hours'], 7.0) def test_time_week_leave_benefit(self): # /!\ this is a week day => it exists an calendar attendance at this time - start = datetime.strptime('2015-11-02 10:00:00', '%Y-%m-%d %H:%M:%S').replace(tzinfo=pytz.utc) # UTC - end = datetime.strptime('2015-11-02 17:00:00', '%Y-%m-%d %H:%M:%S').replace(tzinfo=pytz.utc) + 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) leave_benef = self.env['hr.benefit'].create({ 'name': '1leave', 'employee_id': self.richard_emp.id, @@ -339,13 +359,13 @@ class TestBenefit(TestPayslipBase): 'date_stop': end, }) leave_benef.action_validate(leave_benef.ids) - data = self.richard_emp.get_benefit_days_data(self.benefit_type_leave, self.start, self.end) + data = self.richard_emp._get_benefit_days_data(self.benefit_type_leave, self.start, self.end) self.assertEqual(data['hours'], 5.0, "It should equal the number of hours richard should have worked") def test_time_weekend_leave_benefit(self): # /!\ this is in the weekend => no calendar attendance at this time - start = datetime.strptime('2015-11-01 10:00:00', '%Y-%m-%d %H:%M:%S').replace(tzinfo=pytz.utc) # UTC - end = datetime.strptime('2015-11-01 17:00:00', '%Y-%m-%d %H:%M:%S').replace(tzinfo=pytz.utc) + 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) leave_benef = self.env['hr.benefit'].create({ 'name': '1leave', 'employee_id': self.richard_emp.id, @@ -355,13 +375,13 @@ class TestBenefit(TestPayslipBase): 'date_stop': end, }) leave_benef.action_validate(leave_benef.ids) - data = self.richard_emp.get_benefit_days_data(self.benefit_type_leave, self.start, self.end) + data = self.richard_emp._get_benefit_days_data(self.benefit_type_leave, self.start, self.end) self.assertEqual(data['hours'], 0.0, "It should equal the number of hours richard should have worked") def test_payslip_generation_with_leave(self): # /!\ this is a week day => it exists an calendar attendance at this time - start = datetime.strptime('2015-11-02 10:00:00', '%Y-%m-%d %H:%M:%S').replace(tzinfo=pytz.utc) # UTC - end = datetime.strptime('2015-11-02 17:00:00', '%Y-%m-%d %H:%M:%S').replace(tzinfo=pytz.utc) + 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) leave_benef = self.env['hr.benefit'].create({ 'name': '1leave', 'employee_id': self.richard_emp.id, @@ -372,9 +392,9 @@ class TestBenefit(TestPayslipBase): }) leave_benef.action_validate(leave_benef.ids) payslip_wizard = self.env['hr.payslip.employees'].create({'employee_ids': [(4, self.richard_emp.id)]}) - payslip_wizard.with_context({'default_date_start': start.strftime('%Y-%m-%d'),'default_date_end': end.strftime('%Y-%m-%d')}).compute_sheet() + payslip_wizard.with_context({'default_date_start': Date.to_string(start), 'default_date_end': Date.to_string(end)}).compute_sheet() payslip = self.env['hr.payslip'].search([('employee_id', '=', self.richard_emp.id)]) - work_line = payslip.worked_days_line_ids.filtered(lambda l: l.code == 'WORK100') # From default calendar.attendance + work_line = payslip.worked_days_line_ids.filtered(lambda l: l.code == 'WORK100') # From default calendar.attendance leave_line = payslip.worked_days_line_ids.filtered(lambda l: l.code == 'LEAVE100') self.assertTrue(work_line, "It should have a work line in the payslip") @@ -384,8 +404,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 = datetime.strptime('2015-11-01 10:00:00', '%Y-%m-%d %H:%M:%S').replace(tzinfo=pytz.utc) # UTC - end = datetime.strptime('2015-11-01 17:00:00', '%Y-%m-%d %H:%M:%S').replace(tzinfo=pytz.utc) + 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) benef = self.env['hr.benefit'].create({ 'name': 'Extra', 'employee_id': self.richard_emp.id, @@ -397,8 +417,8 @@ class TestBenefit(TestPayslipBase): benef.action_validate(benef.ids) payslip_wizard = self.env['hr.payslip.employees'].create({'employee_ids': [(4, self.richard_emp.id)]}) payslip_wizard.with_context({ - 'default_date_start': start.strftime('%Y-%m-%d'), - 'default_date_end': (end + relativedelta(days=1)).strftime('%Y-%m-%d') + 'default_date_start': Date.to_string(start), + 'default_date_end': Date.to_string(end + relativedelta(days=1)) }).compute_sheet() payslip = self.env['hr.payslip'].search([('employee_id', '=', self.richard_emp.id)]) work_line = payslip.worked_days_line_ids.filtered(lambda l: l.code == 'WORK100') # From default calendar.attendance diff --git a/addons/hr_payroll/tests/test_leave.py b/addons/hr_payroll/tests/test_leave.py new file mode 100644 index 00000000000..1168d05fee0 --- /dev/null +++ b/addons/hr_payroll/tests/test_leave.py @@ -0,0 +1,53 @@ +# # -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from datetime import datetime +import pytz +from dateutil.relativedelta import relativedelta +from odoo import exceptions +from odoo.tests.common import tagged +from odoo.addons.hr_payroll.tests.common import TestPayslipBase + + +class TestPayrollLeave(TestPayslipBase): + + def create_leave(self): + return self.env['hr.leave'].create({ + 'name': 'Holiday !!!', + 'employee_id': self.richard_emp.id, + 'holiday_status_id': self.leave_type.id, + 'date_to': datetime.today() + relativedelta(days=1), + 'date_from': datetime.today(), + 'number_of_days': 1, + }) + + def test_resource_leave_has_benefit_type(self): + leave = self.create_leave() + + resource_leave = leave._create_resource_leave() + self.assertEqual(resource_leave.benefit_type_id, self.leave_type.benefit_type_id, "it should have the corresponding benefit type") + + def test_resource_leave_in_contract_calendar(self): + other_calendar = self.env['resource.calendar'].create({'name': 'New calendar'}) + contract = self.richard_emp.contract_ids[0] + contract.resource_calendar_id = other_calendar + contract.state = 'open' # this set richard's calendar to New calendar + leave = self.create_leave() + + resource_leave = leave._create_resource_leave() + self.assertEqual(len(resource_leave), 1, "it should have created only one resource leave") + self.assertEqual(resource_leave.benefit_type_id, self.leave_type.benefit_type_id, "it should have the corresponding benefit type") + + def test_resource_leave_different_calendars(self): + other_calendar = self.env['resource.calendar'].create({'name': 'New calendar'}) + contract = self.richard_emp.contract_ids[0] + contract.resource_calendar_id = other_calendar + contract.state = 'open' # this set richard's calendar to New calendar + + # set another calendar + self.richard_emp.resource_calendar_id = self.env['resource.calendar'].create({'name': 'Other calendar'}) + + leave = self.create_leave() + resource_leave = leave._create_resource_leave() + self.assertEqual(len(resource_leave), 2, "it should have created one resource leave per calendar") + self.assertEqual(resource_leave.mapped('benefit_type_id'), self.leave_type.benefit_type_id, "they should have the corresponding benefit type") diff --git a/addons/hr_payroll/views/hr_benefit_views.xml b/addons/hr_payroll/views/hr_benefit_views.xml index c6fd9023e5b..b5d9b018dea 100644 --- a/addons/hr_payroll/views/hr_benefit_views.xml +++ b/addons/hr_payroll/views/hr_benefit_views.xml @@ -53,8 +53,7 @@ - - + @@ -75,7 +74,7 @@
-
diff --git a/addons/hr_payroll/wizard/hr_payroll_contribution_register_report.py b/addons/hr_payroll/wizard/hr_payroll_contribution_register_report.py index a41147c68fc..73807b81845 100644 --- a/addons/hr_payroll/wizard/hr_payroll_contribution_register_report.py +++ b/addons/hr_payroll/wizard/hr_payroll_contribution_register_report.py @@ -1,20 +1,20 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -from datetime import datetime from dateutil import relativedelta -from odoo import api, fields, models +from odoo.fields import Date +from odoo import api, models class PayslipLinesContributionRegister(models.TransientModel): _name = 'payslip.lines.contribution.register' _description = 'Payslip Lines by Contribution Registers' - date_from = fields.Date(string='Date From', required=True, - default=datetime.now().strftime('%Y-%m-01')) - date_to = fields.Date(string='Date To', required=True, - default=str(datetime.now() + relativedelta.relativedelta(months=+1, day=1, days=-1))[:10]) + date_from = Date(string='Date From', required=True, + default=Date.today().replace(day=1)) + date_to = Date(string='Date To', required=True, + default=Date.today() + relativedelta.relativedelta(months=+1, day=1, days=-1)) @api.multi def print_report(self): diff --git a/addons/hr_payroll/wizard/hr_payroll_payslips_by_employees.py b/addons/hr_payroll/wizard/hr_payroll_payslips_by_employees.py index 6cf9ed9301c..00ec37eb0cd 100644 --- a/addons/hr_payroll/wizard/hr_payroll_payslips_by_employees.py +++ b/addons/hr_payroll/wizard/hr_payroll_payslips_by_employees.py @@ -3,7 +3,6 @@ from odoo import api, fields, models, _ from odoo.exceptions import UserError -from datetime import datetime, date class HrPayslipEmployees(models.TransientModel): @@ -24,8 +23,8 @@ class HrPayslipEmployees(models.TransientModel): def compute_sheet(self): self.ensure_one() if not self.env.context.get('active_id'): - from_date = datetime.strptime(self.env.context.get('default_date_start'), '%Y-%m-%d') - end_date = datetime.strptime(self.env.context.get('default_date_end'), '%Y-%m-%d') + from_date = fields.Date.to_date(self.env.context.get('default_date_start')) + end_date = fields.Date.to_date(self.env.context.get('default_date_end')) payslip_run = self.env['hr.payslip.run'].create({ 'name': from_date.strftime('%B %Y'), 'date_start': from_date, diff --git a/addons/hr_payroll_account/models/hr_payroll_account.py b/addons/hr_payroll_account/models/hr_payroll_account.py index 9263b8f69e3..2c992ae06e2 100644 --- a/addons/hr_payroll_account/models/hr_payroll_account.py +++ b/addons/hr_payroll_account/models/hr_payroll_account.py @@ -54,13 +54,13 @@ class HrPayslip(models.Model): @api.multi def action_payslip_done(self): res = super(HrPayslip, self).action_payslip_done() - precision = self.env['decimal.precision'].precision_get('Payroll') for slip in self: line_ids = [] debit_sum = 0.0 credit_sum = 0.0 date = slip.date or slip.date_to + currency = slip.company_id.currency_id name = _('Payslip of %s') % (slip.employee_id.name) move_dict = { @@ -70,8 +70,8 @@ class HrPayslip(models.Model): 'date': date, } for line in slip.line_ids.filtered(lambda line: line.category_id): - amount = slip.credit_note and -line.total or line.total - if float_is_zero(amount, precision_digits=precision): + amount = currency.round(slip.credit_note and -line.total or line.total) + if currency.is_zero(amount): continue debit_account_id = line.salary_rule_id.account_debit.id credit_account_id = line.salary_rule_id.account_credit.id @@ -106,7 +106,7 @@ class HrPayslip(models.Model): line_ids.append(credit_line) credit_sum += credit_line[2]['credit'] - credit_line[2]['debit'] - if float_compare(credit_sum, debit_sum, precision_digits=precision) == -1: + if currency.compare_amounts(credit_sum, debit_sum) == -1: acc_id = slip.journal_id.default_credit_account_id.id if not acc_id: raise UserError(_('The Expense Journal "%s" has not properly configured the Credit Account!') % (slip.journal_id.name)) @@ -117,11 +117,11 @@ class HrPayslip(models.Model): 'journal_id': slip.journal_id.id, 'date': date, 'debit': 0.0, - 'credit': debit_sum - credit_sum, + 'credit': currency.round(debit_sum - credit_sum), }) line_ids.append(adjust_credit) - elif float_compare(debit_sum, credit_sum, precision_digits=precision) == -1: + elif currency.compare_amounts(debit_sum, credit_sum) == -1: acc_id = slip.journal_id.default_debit_account_id.id if not acc_id: raise UserError(_('The Expense Journal "%s" has not properly configured the Debit Account!') % (slip.journal_id.name)) @@ -131,7 +131,7 @@ class HrPayslip(models.Model): 'account_id': acc_id, 'journal_id': slip.journal_id.id, 'date': date, - 'debit': credit_sum - debit_sum, + 'debit': currency.round(credit_sum - debit_sum), 'credit': 0.0, }) line_ids.append(adjust_debit) diff --git a/addons/hr_skills/static/src/css/hr_skills.scss b/addons/hr_skills/static/src/css/hr_skills.scss index 12a7496bd84..cb1a7072922 100644 --- a/addons/hr_skills/static/src/css/hr_skills.scss +++ b/addons/hr_skills/static/src/css/hr_skills.scss @@ -11,6 +11,10 @@ background-color: #e9ecef; } + td.o_skill_cell { + width: 33%; + } + .o_group_resume tbody tr.o_resume_group_header th { font-weight: normal; } diff --git a/addons/hr_skills/static/src/xml/skills_templates.xml b/addons/hr_skills/static/src/xml/skills_templates.xml index 90e29117630..b6d89e04a3d 100644 --- a/addons/hr_skills/static/src/xml/skills_templates.xml +++ b/addons/hr_skills/static/src/xml/skills_templates.xml @@ -4,10 +4,10 @@
+ +
- + + + View diff --git a/addons/mail/models/mail_channel.py b/addons/mail/models/mail_channel.py index 2e91a4d9c05..5ba1fd08ea9 100644 --- a/addons/mail/models/mail_channel.py +++ b/addons/mail/models/mail_channel.py @@ -756,7 +756,11 @@ class Channel(models.Model): self.ensure_one() if self.channel_message_ids.ids: last_message_id = self.channel_message_ids.ids[0] # zero is the index of the last message - self.env['mail.channel.partner'].search([('channel_id', 'in', self.ids), ('partner_id', '=', self.env.user.partner_id.id)]).write({ + channel_partner = self.env['mail.channel.partner'].search([('channel_id', 'in', self.ids), ('partner_id', '=', self.env.user.partner_id.id)]) + if channel_partner.seen_message_id.id == last_message_id: + # last message seen by user is already up-to-date + return + channel_partner.write({ 'seen_message_id': last_message_id, 'fetched_message_id': last_message_id, }) @@ -765,26 +769,37 @@ class Channel(models.Model): 'last_message_id': last_message_id, 'partner_id': self.env.user.partner_id.id, } - self.env['bus.bus'].sendmany([[(self._cr.dbname, 'mail.channel', self.id), data]]) + if self.channel_type == 'chat': + self.env['bus.bus'].sendmany([[(self._cr.dbname, 'mail.channel', self.id), data]]) + else: + data['channel_id'] = self.id + self.env['bus.bus'].sendone((self._cr.dbname, 'res.partner', self.env.user.partner_id.id), data) return last_message_id @api.multi def channel_fetched(self): """ Broadcast the channel_fetched notification to channel members + :param channel_ids : list of channel id that has been fetched by current user """ - self.ensure_one() - if not self.channel_message_ids.ids: - return - last_message_id = self.channel_message_ids.ids[0] # zero is the index of the last message - self.env['mail.channel.partner'].search([('channel_id', 'in', self.ids), ('partner_id', '=', self.env.user.partner_id.id)]).write({ - 'fetched_message_id': last_message_id, - }) - data = { - 'info': 'channel_fetched', - 'last_message_id': last_message_id, - 'partner_id': self.env.user.partner_id.id, - } - self.env['bus.bus'].sendmany([[(self._cr.dbname, 'mail.channel', self.id), data]]) + for channel in self: + if not channel.channel_message_ids.ids: + return + if channel.channel_type != 'chat': + return + last_message_id = channel.channel_message_ids.ids[0] # zero is the index of the last message + channel_partner = self.env['mail.channel.partner'].search([('channel_id', '=', channel.id), ('partner_id', '=', self.env.user.partner_id.id)]) + if channel_partner.fetched_message_id.id == last_message_id: + # last message fetched by user is already up-to-date + return + channel_partner.write({ + 'fetched_message_id': last_message_id, + }) + data = { + 'info': 'channel_fetched', + 'last_message_id': last_message_id, + 'partner_id': self.env.user.partner_id.id, + } + self.env['bus.bus'].sendmany([[(self._cr.dbname, 'mail.channel', channel.id), data]]) @api.multi def channel_invite(self, partner_ids): diff --git a/addons/mail/models/mail_message.py b/addons/mail/models/mail_message.py index 24b6a069d46..3c37988a5bb 100644 --- a/addons/mail/models/mail_message.py +++ b/addons/mail/models/mail_message.py @@ -559,7 +559,7 @@ class Message(models.Model): 'model': message.model, 'last_message_date': message.date, 'module_icon': '/mail/static/src/img/smiley/mailfailure.jpg', - 'notifications': dict((notif.res_partner_id.id, (notif.email_status, notif.res_partner_id.name)) for notif in message.notification_ids) + 'notifications': dict((notif.res_partner_id.id, (notif.email_status, notif.res_partner_id.name)) for notif in message.notification_ids.sudo()) } failures_infos.append(info) return failures_infos diff --git a/addons/mail/models/mail_thread.py b/addons/mail/models/mail_thread.py index 64db895b321..44c6cec5687 100644 --- a/addons/mail/models/mail_thread.py +++ b/addons/mail/models/mail_thread.py @@ -744,7 +744,9 @@ class MailThread(models.AbstractModel): access_link = self._notify_get_action_link('view') if message.model: - model_name = self.env['ir.model']._get(message.model).display_name + model = self.env['ir.model'].with_context( + lang=self.env.context.get('lang', self.env.user.lang)) + model_name = model._get(message.model).display_name view_title = _('View %s') % model_name else: view_title = _('View') diff --git a/addons/mail/static/src/js/chatter.js b/addons/mail/static/src/js/chatter.js index 330e56b77b4..72971fbeee9 100644 --- a/addons/mail/static/src/js/chatter.js +++ b/addons/mail/static/src/js/chatter.js @@ -90,6 +90,7 @@ var Chatter = Widget.extend({ this._$topbar = this.$('.o_chatter_topbar'); if(!this._disableAttachmentBox) { this.$('.o_topbar_right_area').append(QWeb.render('mail.chatter.Attachment.Button', { + displayCounter: !!this.fields.thread, count: this.record.data.message_attachment_count || 0, })); } @@ -362,7 +363,9 @@ var Chatter = Widget.extend({ if (this._isAttachmentBoxOpen) { this._fetchAttachments().then(this._openAttachmentBox.bind(this)); } - this.trigger_up('reload', { fieldNames: ['message_attachment_count'] }); + if (this.fields.thread) { + this.trigger_up('reload', { fieldNames: ['message_attachment_count'] }); + } }, /** * @private @@ -490,7 +493,9 @@ var Chatter = Widget.extend({ }) .then(function () { self._reloadAttachmentBox(); - self.fields.thread.removeAttachments([ev.data.attachmentId]); + if (self.fields.thread) { + self.fields.thread.removeAttachments([ev.data.attachmentId]); + } self.trigger_up('reload'); }); } diff --git a/addons/mail/static/src/js/models/threads/channel.js b/addons/mail/static/src/js/models/threads/channel.js index 7db3474ebb2..9f0f37264db 100644 --- a/addons/mail/static/src/js/models/threads/channel.js +++ b/addons/mail/static/src/js/models/threads/channel.js @@ -1,7 +1,6 @@ odoo.define('mail.model.Channel', function (require) { "use strict"; -var ChannelSeenMixin = require('mail.model.ChannelSeenMixin'); var SearchableThread = require('mail.model.SearchableThread'); var ThreadTypingMixin = require('mail.model.ThreadTypingMixin'); var mailUtils = require('mail.utils'); @@ -17,7 +16,7 @@ var time = require('web.time'); * Any piece of code in JS that make use of channels must ideally interact with * such objects, instead of direct data from the server. */ -var Channel = SearchableThread.extend(ChannelSeenMixin, ThreadTypingMixin, { +var Channel = SearchableThread.extend(ThreadTypingMixin, { /** * @override * @param {Object} params @@ -50,7 +49,6 @@ var Channel = SearchableThread.extend(ChannelSeenMixin, ThreadTypingMixin, { init: function (params) { var self = this; this._super.apply(this, arguments); - ChannelSeenMixin.init.apply(this, arguments); ThreadTypingMixin.init.apply(this, arguments); var data = params.data; @@ -387,23 +385,9 @@ var Channel = SearchableThread.extend(ChannelSeenMixin, ThreadTypingMixin, { */ _markAsRead: function () { var superDef = this._super.apply(this, arguments); - var seenDef = this._throttleNotifySeen(); + var seenDef = this._notifySeen(); return $.when(superDef, seenDef); }, - /** - * @override {mail.model.ThreadSeenMixin} - * @private - * @returns {$.Promise} - */ - _notifyFetched: function () { - return this._rpc({ - model: 'mail.channel', - method: 'channel_fetched', - args: [[this._id]], - }, { - shadow: true - }); - }, /** * @override {mail.model.ThreadTypingMixin} * @private @@ -420,20 +404,16 @@ var Channel = SearchableThread.extend(ChannelSeenMixin, ThreadTypingMixin, { }, { shadow: true }); }, /** - * @override {mail.model.ThreadSeenMixin} * @private * @returns {$.Promise} resolved with ID of last seen message */ _notifySeen: function () { var self = this; - this._cancelThrottledNotifyFetched(); return this._rpc({ model: 'mail.channel', method: 'channel_seen', args: [[this._id]], - }, { - shadow: true - }).then(function (lastSeenMessageID) { + }, { shadow: true }).then(function (lastSeenMessageID) { self._lastSeenMessageID = lastSeenMessageID; return lastSeenMessageID; }); diff --git a/addons/mail/static/src/js/models/threads/dm_chat.js b/addons/mail/static/src/js/models/threads/dm_chat.js index fb524515193..c0ba9c527b0 100644 --- a/addons/mail/static/src/js/models/threads/dm_chat.js +++ b/addons/mail/static/src/js/models/threads/dm_chat.js @@ -1,6 +1,7 @@ odoo.define('mail.model.DMChat', function (require) { "use strict"; +var ChannelSeenMixin = require('mail.model.ChannelSeenMixin'); var TwoUserChannel = require('mail.model.TwoUserChannel'); var core = require('web.core'); @@ -11,7 +12,7 @@ var _t = core._t; * Any piece of code in JS that make use of DMs must ideally interact with * such objects, instead of direct data from the server. */ -var DMChat = TwoUserChannel.extend({ +var DMChat = TwoUserChannel.extend(ChannelSeenMixin, { /** * @override * @param {Object} params @@ -27,6 +28,7 @@ var DMChat = TwoUserChannel.extend({ */ init: function (params) { this._super.apply(this, arguments); + ChannelSeenMixin.init.apply(this, arguments); var data = params.data; diff --git a/addons/mail/static/src/js/models/threads/mixins/channel_seen_mixin.js b/addons/mail/static/src/js/models/threads/mixins/channel_seen_mixin.js index 8a59db70aa2..20a5bba5407 100644 --- a/addons/mail/static/src/js/models/threads/mixins/channel_seen_mixin.js +++ b/addons/mail/static/src/js/models/threads/mixins/channel_seen_mixin.js @@ -1,8 +1,6 @@ odoo.define('mail.model.ChannelSeenMixin', function (require) { "use strict"; -var CCThrottleFunction = require('mail.model.CCThrottleFunction'); - var session = require('web.session'); /** @@ -25,12 +23,6 @@ var ChannelSeenMixin = { init: function (params) { this._seenPartnersInfo = params.data.seen_partners_info || []; - this._throttleNotifySeen = _.throttle(this._notifySeen.bind(this), 3000); - this._throttleNotifyFetched = CCThrottleFunction({ - duration: 3000, - func: this._notifyFetched.bind(this), - }); - this.on('message_added', this, this._onSeenMessageAdded); }, @@ -220,34 +212,6 @@ var ChannelSeenMixin = { this.call('mail_service', 'getMailBus').trigger('update_channel', this.getID()); }, - //-------------------------------------------------------------------------- - // Private - //-------------------------------------------------------------------------- - - /** - * @private - */ - _cancelThrottledNotifyFetched: function () { - this._throttleNotifyFetched.cancel(); - this._throttleNotifyFetched.clear(); - }, - /** - * @abstract - * @private - * @returns {$.Promise} - */ - _notifyFetched: function () { - return $.when(); - }, - /** - * @abstract - * @private - * @returns {$.Promise} resolved with ID of last seen message - */ - _notifySeen: function () { - return $.when(); - }, - //-------------------------------------------------------------------------- // Handlers //-------------------------------------------------------------------------- @@ -256,7 +220,7 @@ var ChannelSeenMixin = { * @private */ _onSeenMessageAdded: function () { - this._throttleNotifyFetched(); + this.call('mail_service', 'notifyChannelFetched', { channelID: this._id }); }, }; diff --git a/addons/mail/static/src/js/services/mail_manager.js b/addons/mail/static/src/js/services/mail_manager.js index 204a88bb8e9..5eb588d24e7 100644 --- a/addons/mail/static/src/js/services/mail_manager.js +++ b/addons/mail/static/src/js/services/mail_manager.js @@ -53,6 +53,12 @@ var MailManager = AbstractService.extend({ */ start: function () { this._super.apply(this, arguments); + + this._throttleNotifyChannelFetched = _.throttle(this._notifyChannelFetched.bind(this), 3000); + // List of channel that have requested the service to notify when + // it has been fetched, so that throttled RPCs batch the ids. + this._toNotifyChannelFetchedIDs = []; + this._initializeInternalState(); this._listenOnBuses(); this._fetchMailStateFromServer(); @@ -338,6 +344,14 @@ var MailManager = AbstractService.extend({ return $.when(); } }, + /** + * @param {Object} data + * @param {integer} data.channelID + */ + notifyChannelFetched: function (data) { + this._toNotifyChannelFetchedIDs = _.uniq(this._toNotifyChannelFetchedIDs.concat(data.channelID)); + this._throttleNotifyChannelFetched(); + }, /** * @param {integer|string} threadID */ @@ -918,6 +932,23 @@ var MailManager = AbstractService.extend({ _makeMessage: function (data) { return new Message(this, data); }, + /** + * @private + * @returns {$.Promise} + */ + _notifyChannelFetched: function () { + var channelIDs = this._toNotifyChannelFetchedIDs; + this._toNotifyChannelFetchedIDs = []; + if (_.isEmpty(channelIDs)) { + // no channel to notify fetched + return $.when(); + } + return this._rpc({ + model: 'mail.channel', + method: 'channel_fetched', + args: [channelIDs], + }, { shadow: true }); + }, /** * shows a popup to notify a new received message. * This will also rename the browser tab if this is not the active tab. diff --git a/addons/mail/static/src/js/services/mail_notification_manager.js b/addons/mail/static/src/js/services/mail_notification_manager.js index b77fa3e65fd..fb3527b2b90 100644 --- a/addons/mail/static/src/js/services/mail_notification_manager.js +++ b/addons/mail/static/src/js/services/mail_notification_manager.js @@ -80,7 +80,9 @@ MailManager.include({ if (!channel) { return; } - channel.updateSeenPartnersInfo(data); + if (channel.hasSeenFeature()) { + channel.updateSeenPartnersInfo(data); + } }, /** * Called when a new or updated message is received on a channel @@ -126,7 +128,9 @@ MailManager.include({ if (!channel) { return; } - channel.updateSeenPartnersInfo(data); + if (channel.hasSeenFeature()) { + channel.updateSeenPartnersInfo(data); + } if (session.partner_id !== data.partner_id) { return; } @@ -235,6 +239,27 @@ MailManager.include({ }); } }, + /** + * Called when receiving a multi_user_channel seen notification. Only + * the current user is notified. This must be handled as if this is a + * channel seen notification. + * + * Note that this is a 'res.partner' notification because only the current + * user is notified on channel seen. This is a consequence from disabling + * the seen feature on multi_user_channel, because we still need to get + * the last seen message ID in order to display the "New Messages" separator + * in Discuss. + * + * @private + * @param {Object} data + * @param {integer} data.channel_id + * @param {string} data.info 'channel_seen' + * @param {integer} data.last_message_id + * @param {integer} data.partner_id + */ + _handlePartnerChannnelSeenNotification: function (data) { + this._handleChannelSeenNotification(data.channel_id, data); + }, /** * Add or remove failure when receiving a failure update message * @@ -387,6 +412,8 @@ MailManager.include({ this._handlePartnerMailFailureNotification(data); } else if (data.type === 'user_connection') { this._handlePartnerUserConnectionNotification(data); + } else if (data.info === 'channel_seen') { + this._handlePartnerChannnelSeenNotification(data); } else { this._handlePartnerChannelNotification(data); } diff --git a/addons/mail/static/src/js/thread_widget.js b/addons/mail/static/src/js/thread_widget.js index 2e2b7e222c6..cbac2c3b417 100644 --- a/addons/mail/static/src/js/thread_widget.js +++ b/addons/mail/static/src/js/thread_widget.js @@ -103,6 +103,7 @@ var ThreadWidget = Widget.extend({ this._messageSeenPopover.popover('hide'); } this._destroyOpenSeenPopoverIDs(); + this._super(); }, /** * @param {mail.model.AbstractThread} thread the thread to render. diff --git a/addons/mail/static/src/js/thread_windows/thread_window.js b/addons/mail/static/src/js/thread_windows/thread_window.js index 11a7f8a7926..e5496e0bc3c 100644 --- a/addons/mail/static/src/js/thread_windows/thread_window.js +++ b/addons/mail/static/src/js/thread_windows/thread_window.js @@ -402,6 +402,9 @@ var ThreadWindow = AbstractThreadWindow.extend({ * @param {integer} channelID */ _onUpdateChannel: function (channelID) { + if (!this.hasThread()) { + return; + } if (this._thread.getID() !== channelID) { return; } diff --git a/addons/mail/static/src/scss/thread.scss b/addons/mail/static/src/scss/thread.scss index 84a826922ff..04ffee37478 100644 --- a/addons/mail/static/src/scss/thread.scss +++ b/addons/mail/static/src/scss/thread.scss @@ -294,7 +294,7 @@ opacity: 0.6; &.o_all_seen { - color: $o-enterprise-primary-color; + color: $o-enterprise-color; } &:hover { @@ -308,7 +308,7 @@ .fa-check:nth-child(2) { position: absolute; - top: 1px; + top: -1px; left: 0px; } } diff --git a/addons/mail/static/src/xml/chatter.xml b/addons/mail/static/src/xml/chatter.xml index 7f8629f1bae..7fe1eeebd77 100644 --- a/addons/mail/static/src/xml/chatter.xml +++ b/addons/mail/static/src/xml/chatter.xml @@ -105,11 +105,17 @@ +

dom not to edit




', - steps: [{ - start: 'p:eq(1)->3', - key: 'BACKSPACE', - }, { - key: 'BACKSPACE', - }, { - key: 'BACKSPACE', - }], - test: { - content: '

dom not to edit


', - start: "p:eq(1):contents()[0]->0", - }, - }, { name: "in complex-dom (empty-td (td before) -> 2x SHIFT-ENTER): 3x BACKSPACE -> 'a'", content: '

dom not to edit




', @@ -3126,6 +3110,55 @@ var keyboardTestsBackspace = [{ start: "p:eq(1):contents()[0]->1", }, }, + { + name: "in h1: BACKSPACE on full selection -> 'a'", + content: '

dom to delete

', + steps: [{ + start: 'h1:contents()[0]->0', + end: 'h1:contents()[0]->13', + key: 'BACKSPACE', + }, { + key: 'a', + }], + test: { + content: '

a

', + start: "h1:contents()[0]->1", + }, + }, + { + name: "in h1: BACKSPACE on full selection -> BACKSPACE -> 'a'", + content: '

dom to delete

', + steps: [{ + start: 'h1:contents()[0]->0', + end: 'h1:contents()[0]->13', + key: 'BACKSPACE', + }, { + key: 'BACKSPACE', + }, { + key: 'a', + }], + test: { + content: '

a

', + start: "p:contents()[0]->1", + }, + }, + { + name: "in h1: BACKSPACE on full selection -> DELETE -> 'a'", + content: '

dom to delete

', + steps: [{ + start: 'h1:contents()[0]->0', + end: 'h1:contents()[0]->13', + key: 'BACKSPACE', + }, { + key: 'DELETE', + }, { + key: 'a', + }], + test: { + content: '

a

', + start: "h1:contents()[0]->1", + }, + }, // merging non-similar blocks diff --git a/addons/website/controllers/main.py b/addons/website/controllers/main.py index a2d1d332f80..13e82dc5c12 100644 --- a/addons/website/controllers/main.py +++ b/addons/website/controllers/main.py @@ -283,24 +283,36 @@ class Website(Home): views = views.sorted(key=lambda v: (v.inherit_id.id, v.name)) return views.read(['name', 'id', 'key', 'xml_id', 'arch', 'active', 'inherit_id']) - @http.route('/website/reset_templates', type='http', auth='user', methods=['POST'], website=True) + @http.route('/website/reset_templates', type='http', auth='user', methods=['POST'], website=True, csrf=False) def reset_template(self, templates, redirect='/', **kwargs): + """ This method will try to reset a list of broken views ids. + It will read the original `arch` from the view's XML file (arch_fs). + Views without an `arch_fs` can't be reset, except views created when + dropping a snippet in specific oe_structure that create an inherited + view doing an xpath. + Note: The `arch_fs` field is automatically erased when there is a + write on the `arch` field. + + This method is typically useful to reset specific views. In that case we + read the XML file from the generic view. + """ templates = request.httprequest.form.getlist('templates') - modules_to_update = [] for temp_id in templates: view = request.env['ir.ui.view'].browse(int(temp_id)) - if view.page: + if 'oe_structure' in view.key: + # Particular xpathing view created in edit mode + view.unlink() continue - view.model_data_id.write({ - 'noupdate': False - }) - if view.model_data_id.module not in modules_to_update: - modules_to_update.append(view.model_data_id.module) + xml_view = view._get_original_view() # view might already be the xml_view + if xml_view.arch_fs: + view_file_arch = xml_view.with_context(read_arch_from_file=True).arch + # Deactivate COW to not fix a generic view by creating a specific + view.with_context(website_id=None).arch_db = view_file_arch + if view == xml_view: + view.model_data_id.write({ + 'noupdate': False + }) - if modules_to_update: - modules = request.env['ir.module.module'].sudo().search([('name', 'in', modules_to_update)]) - if modules: - modules.button_immediate_upgrade() return request.redirect(redirect) @http.route(['/website/publish'], type='json', auth="public", website=True) diff --git a/addons/website/models/ir_http.py b/addons/website/models/ir_http.py index be3085eb835..2be6c571a37 100644 --- a/addons/website/models/ir_http.py +++ b/addons/website/models/ir_http.py @@ -2,6 +2,7 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. import logging +from lxml import etree import traceback import os import unittest @@ -12,14 +13,14 @@ import werkzeug.routing import werkzeug.utils import odoo -from odoo import api, models +from odoo import api, models, registry from odoo import SUPERUSER_ID from odoo.http import request from odoo.tools import config -from odoo.exceptions import QWebException from odoo.tools.safe_eval import safe_eval from odoo.osv.expression import FALSE_DOMAIN, OR +from odoo.addons.base.models.qweb import QWebException from odoo.addons.http_routing.models.ir_http import ModelConverter, _guess_mimetype from odoo.addons.portal.controllers.portal import _build_url_w_params @@ -185,7 +186,7 @@ class Http(models.AbstractModel): # if parent excplicitely returns a plain response, then we don't touch it return response except Exception as e: - if 'werkzeug' in config['dev_mode'] and (not isinstance(exception, QWebException) or not exception.qweb.get('cause')): + if 'werkzeug' in config['dev_mode']: raise e exception = e @@ -207,19 +208,9 @@ class Http(models.AbstractModel): if isinstance(exception, QWebException): values.update(qweb_exception=exception) - if isinstance(exception.qweb.get('cause'), odoo.exceptions.AccessError): + if type(exception.error) == odoo.exceptions.AccessError: code = 403 - if code == 500: - logger.error("500 Internal Server Error:\n\n%s", values['traceback']) - if 'qweb_exception' in values: - view = request.env["ir.ui.view"] - views = view._views_get(exception.qweb['template']) - to_reset = views.filtered(lambda view: view.model_data_id.noupdate is True and view.arch_fs) - values['views'] = to_reset - elif code == 403: - logger.warn("403 Forbidden:\n\n%s", values['traceback']) - values.update( status_message=werkzeug.http.HTTP_STATUS_CODES[code], status_code=code, @@ -233,10 +224,51 @@ class Http(models.AbstractModel): if not request.uid: cls._auth_method_public() - try: - html = request.env['ir.ui.view'].render_template('website.%s' % view_id, values) - except Exception: - html = request.env['ir.ui.view'].render_template('website.http_error', values) + with registry(request.env.cr.dbname).cursor() as cr: + env = api.Environment(cr, request.uid, request.env.context) + if code == 500: + logger.error("500 Internal Server Error:\n\n%s", values['traceback']) + View = env["ir.ui.view"] + if 'qweb_exception' in values: + if 'load could not load template' in exception.args: + # When t-calling an inexisting template, we don't have reference to + # the view that did the t-call. We need to find it. + values['views'] = View.search([ + ('type', '=', 'qweb'), + '|', + ('arch_db', 'ilike', 't-call="%s"' % exception.name), + ('arch_db', 'ilike', "t-call='%s'" % exception.name) + ], order='write_date desc', limit=1) + else: + try: + # exception.name might be int, string + exception_template = int(exception.name) + except: + exception_template = exception.name + view = View._view_obj(exception_template) + et = etree.fromstring(view.with_context(inherit_branding=False).read_combined(['arch'])['arch']) + node = et.xpath(exception.path) + line = node is not None and etree.tostring(node[0], encoding='unicode') + # line = exception.html # FALSE -> contains branding
+ if line: + # If QWebException occurs in a child view, the parent view is raised + values['editable'] = request.uid and request.website.is_publisher() + values['views'] = View._views_get(exception_template).filtered( + lambda v: line in v.arch + ) + else: + values['views'] = view + # Keep only views that we can reset + values['views'] = values['views'].filtered( + lambda view: view._get_original_view().arch_fs or 'oe_structure' in view.key + ) + elif code == 403: + logger.warn("403 Forbidden:\n\n%s", values['traceback']) + try: + html = env['ir.ui.view'].render_template('website.%s' % view_id, values) + except Exception: + html = env['ir.ui.view'].render_template('website.http_error', values) + return werkzeug.wrappers.Response(html, status=code, content_type='text/html;charset=utf-8') def binary_content(self, xmlid=None, model='ir.attachment', id=None, field='datas', @@ -250,7 +282,7 @@ class Http(models.AbstractModel): obj = self.env[model].browse(int(id)) if obj and 'website_published' in obj._fields: if self.env[obj._name].sudo().search([('id', '=', obj.id), ('website_published', '=', True)]): - self.sudo() + self = self.sudo() return super(Http, self).binary_content( xmlid=xmlid, model=model, id=id, field=field, unique=unique, filename=filename, filename_field=filename_field, download=download, mimetype=mimetype, diff --git a/addons/website/models/ir_ui_view.py b/addons/website/models/ir_ui_view.py index dc3ebb286c3..27fe7a66437 100644 --- a/addons/website/models/ir_ui_view.py +++ b/addons/website/models/ir_ui_view.py @@ -277,6 +277,16 @@ class View(models.Model): return view.id return super(View, self).get_view_id(xml_id) + @api.multi + def _get_original_view(self): + """Given a view, retrieve the original view it was COW'd from. + The given view might already be the original one. In that case it will + (and should) return itself. + """ + self.ensure_one() + domain = [('key', '=', self.key), ('model_data_id', '!=', None)] + return self.search(domain, limit=1) # Useless limit has multiple xmlid should not be possible + @api.multi def render(self, values=None, engine='ir.qweb', minimal_qcontext=False): """ Render the template. If website is enabled on request, then extend rendering context with website values. """ @@ -371,6 +381,15 @@ class View(models.Model): res['website_id'] = self.env['website'].get_current_website().id return res + @api.model + def _set_noupdate(self): + '''If website is installed, any call to `save` from the frontend will + actually write on the specific view (or create it if not exist yet). + In that case, we don't want to flag the generic view as noupdate. + ''' + if not self._context.get('website_id'): + super(View, self)._set_noupdate() + @api.multi def save(self, value, xpath=None): self.ensure_one() diff --git a/addons/website/models/res_partner.py b/addons/website/models/res_partner.py index 9256f80293d..d62ee637675 100644 --- a/addons/website/models/res_partner.py +++ b/addons/website/models/res_partner.py @@ -51,3 +51,10 @@ class Partner(models.Model): # onchange uses the cache to retrieve value, we need to copy computed_value into the initial env for record, record2 in zip(self, self2): record.display_name = record2.display_name + + @api.multi + def get_base_url(self): + """When using multi-website, we want the user to be redirected to the + most appropriate website if possible.""" + res = super(Partner, self).get_base_url() + return self.website_id and self.website_id._get_http_domain() or res diff --git a/addons/website/models/website.py b/addons/website/models/website.py index 4eeb73c5423..0261b1fc616 100644 --- a/addons/website/models/website.py +++ b/addons/website/models/website.py @@ -454,7 +454,8 @@ class Website(models.Model): if website_id: return self.browse(website_id) - domain_name = request and request.httprequest.environ.get('HTTP_HOST', '').split(':')[0] or None + # The format of `httprequest.host` is `domain:port` + domain_name = request and request.httprequest.host or '' country = request.session.geoip.get('country_code') if request and request.session.geoip else False country_id = False @@ -465,9 +466,64 @@ class Website(models.Model): return self.browse(website_id) @tools.cache('domain_name', 'country_id', 'fallback') + @api.model def _get_current_website_id(self, domain_name, country_id, fallback=True): - # sort on country_group_ids so that we fall back on a generic website (empty country_group_ids) - websites = self.search([('domain', '=', domain_name)]).sorted('country_group_ids') + """Get the current website id. + + First find all the websites for which the configured `domain` (after + ignoring a potential scheme) is equal to the given + `domain_name`. If there is only one result, return it immediately. + + If there are no website found for the given `domain_name`, either + fallback to the first found website (no matter its `domain`) or return + False depending on the `fallback` parameter. + + If there are multiple websites for the same `domain_name`, we need to + filter them out by country. We return the first found website matching + the given `country_id`. If no found website matching `domain_name` + corresponds to the given `country_id`, the first found website for + `domain_name` will be returned (no matter its country). + + :param domain_name: the domain for which we want the website. + In regard to the `url_parse` method, only the `netloc` part should + be given here, no `scheme`. + :type domain_name: string + + :param country_id: id of the country for which we want the website + :type country_id: int + + :param fallback: if True and no website is found for the specificed + `domain_name`, return the first website (without filtering them) + :type fallback: bool + + :return: id of the found website, or False if no website is found and + `fallback` is False + :rtype: int or False + + :raises: if `fallback` is True but no website at all is found + """ + def _remove_port(domain_name): + return (domain_name or '').split(':')[0] + + def _filter_domain(website, domain_name, ignore_port=False): + """Ignore `scheme` from the `domain`, just match the `netloc` which + is host:port in the version of `url_parse` we use.""" + # Here we add http:// to the domain if it's not set because + # `url_parse` expects it to be set to correctly return the `netloc`. + website_domain = urls.url_parse(website._get_http_domain()).netloc + if ignore_port: + website_domain = _remove_port(website_domain) + domain_name = _remove_port(domain_name) + return website_domain.lower() == (domain_name or '').lower() + + # Sort on country_group_ids so that we fall back on a generic website: + # websites with empty country_group_ids will be first. + found_websites = self.search([('domain', 'ilike', _remove_port(domain_name))]).sorted('country_group_ids') + # Filter for the exact domain (to filter out potential subdomains) due + # to the use of ilike. + websites = found_websites.filtered(lambda w: _filter_domain(w, domain_name)) + # If there is no domain matching for the given port, ignore the port. + websites = websites or found_websites.filtered(lambda w: _filter_domain(w, domain_name, ignore_port=True)) if not websites: if not fallback: @@ -728,3 +784,16 @@ class Website(models.Model): 'url': '/', 'target': 'self', } + + @api.multi + def _get_http_domain(self): + """Get the domain of the current website, prefixed by http if no + scheme is specified. + + Empty string if no domain is specified on the website. + """ + self.ensure_one() + if not self.domain: + return '' + res = urls.url_parse(self.domain) + return 'http://' + self.domain if not res.scheme else self.domain diff --git a/addons/website/static/src/js/tours/website_tour_reset_password.js b/addons/website/static/src/js/tours/website_tour_reset_password.js new file mode 100644 index 00000000000..307b741ceef --- /dev/null +++ b/addons/website/static/src/js/tours/website_tour_reset_password.js @@ -0,0 +1,172 @@ +odoo.define('website.tour_reset_password', function (require) { +'use strict'; + +var localStorage = require('web.local_storage'); +var rpc = require('web.rpc'); +var tour = require('web_tour.tour'); +require('web.dom_ready'); + +var currentDomain = window.location.protocol + '//' + window.location.hostname; +var emailsUrl = '/web#action=mail.action_view_mail_mail&view_type=list'; +var usersUrl = '/web#action=base.action_res_users&view_type=list'; +var resetLinkKey = 'website.tour_reset_password.resetLink'; + +tour.register('website_reset_password', { + test: true, + url: '/web', +}, +[ + { + content: "Change the domain of the websites and go to users page", + trigger: '.oe_topbar_name:contains("Admin")', + run: function () { + // We could do it with the UI but this is not the goal of this test, + // so we just make an RPC to be faster. + + // We change the domain of the website to test that the email that + // will be sent uses the correct domain for its links. + var def1 = rpc.query({ + 'model': 'website', + 'method': 'write', + 'args': [[1], { + 'domain': "my-test-domain.com", + }], + }); + // We need to change the domain of all the websites otherwise the + // website selector will return the website 2 since the domain we + // set on website 1 doesn't actually match our test server. + var def2 = rpc.query({ + 'model': 'website', + 'method': 'write', + 'args': [[2], { + 'domain': "https://domain-not-used.fr", + }], + }); + // Set a website on "Admin" partner to test the URL of the website. + var def3 = rpc.query({ + model: 'res.partner', + method: 'name_search', + kwargs: {'name': 'Mitchell Admin'}, + }).then(function (res) { + return rpc.query({ + 'model': 'res.partner', + 'method': 'write', + 'args': [[res[0][0]], { + 'website_id': 1, + }], + }); + }); + + return $.when(def1, def2, def3).then(function () { + window.location.href = usersUrl; + }); + }, + }, + { + content: "click on Admin", + trigger: '.o_data_cell:contains("Admin")', + }, + { + content: "click on reset password", + trigger: '.btn[name="action_reset_password"]', + }, + { + content: "wait mail to be sent, and go see it", + trigger: 'a[name="signup_url"][href^="http://my-test-domain.com"]', + run: function () { + window.location.href = emailsUrl; + }, + }, + { + content: "click on the first email", + trigger: '.o_data_cell:contains("Password reset"):eq(0)', + }, + { + content: "check email has the button", + trigger: 'div.oe_form_field_html[name="body_html"] a:contains("Change password")', + run: function () {}, + }, + { + content: "check the URL is correct too", + trigger: 'div.oe_form_field_html[name="body_html"] a:contains("Change password")[href^="http://my-test-domain.com"]', + run: function () { + // reset the domain of the websites, go to users page + return rpc.query({ + 'model': 'website', + 'method': 'write', + 'args': [[1, 2], { + 'domain': "", + }], + }).then(function () { + window.location.href = usersUrl; + }); + }, + }, + { + content: "click on Admin", + trigger: '.o_data_cell:contains("Admin")', + }, + { + content: "click on reset password", + trigger: '.btn[name="action_reset_password"]', + }, + { + content: "wait mail to be sent, and go see it", + trigger: 'a[name="signup_url"][href^="' + currentDomain + '"]', + run: function () { + window.location.href = emailsUrl; + }, + }, + { + content: "click on the first email", + trigger: '.o_data_cell:contains("Password reset"):eq(0)', + }, + { + content: "check the link has the current host, save the link, logout", + trigger: 'div.oe_form_field_html[name="body_html"] a:contains("Change password")[href^="' + currentDomain + '"]', + run: function () { + var link = $('div.oe_form_field_html[name="body_html"] a:contains("Change password")').attr('href'); + localStorage.setItem(resetLinkKey, link); + window.location.href = "/web/session/logout?redirect=/"; + }, + }, + { + content: "go to the reset link", + trigger: 'a[href="/web/login"]', + run: function () { + window.location.href = localStorage.getItem(resetLinkKey); + }, + }, + { + content: "fill new password and submit", + trigger: '.oe_reset_password_form', + run: function () { + var $form = $('.oe_reset_password_form'); + $form.find('input[name="password"]').val('adminadmin'); + // password must be at least 8 if the module `auth_password_policy` + // is installed which is the case on runbot + $form.find('input[name="confirm_password"]').val('adminadmin'); + $form.find('button[type="submit"]').click(); + }, + }, + { + content: "check logged in, and reset admin website", + trigger: '.oe_topbar_name:contains("Admin")', + run: function () { + return rpc.query({ + model: 'res.partner', + method: 'name_search', + kwargs: {'name': 'Admin'}, + }).then(function (res) { + return rpc.query({ + 'model': 'res.partner', + 'method': 'write', + 'args': [[res[0][0]], { + 'website_id': false, + }], + }); + }); + }, + }, +]); +}); diff --git a/addons/website/static/src/scss/website.editor.ui.scss b/addons/website/static/src/scss/website.editor.ui.scss index aa5cba39e00..353be1efed1 100644 --- a/addons/website/static/src/scss/website.editor.ui.scss +++ b/addons/website/static/src/scss/website.editor.ui.scss @@ -34,7 +34,7 @@ &:checked+span { box-shadow: none; - background-image: linear-gradient(0deg, $o-we-color-success, darken($o-we-color-success, 10%)); + background: $o-we-color-success; &:after { margin-left: ($o-we-switch-size*1.8 - $o-we-switch-size) + 0.1; @@ -46,7 +46,7 @@ >input { &:not(:checked)+span { box-shadow: none; - background-image: linear-gradient(0deg, lighten(theme-color('danger'), 5%), darken(theme-color('danger'), 5%)); + background: $o-we-color-danger; } } } diff --git a/addons/website/static/src/scss/website.ui.scss b/addons/website/static/src/scss/website.ui.scss index 6865a8b7e79..cb6fbb94fcb 100644 --- a/addons/website/static/src/scss/website.ui.scss +++ b/addons/website/static/src/scss/website.ui.scss @@ -134,10 +134,14 @@ body .modal { cursor: pointer; font-family: $o-we-font-family; + &, .close { + color: white; + } + h4 { - color: lightgray; - font-family:inherit; + font-family: inherit; font-weight: normal; + color: inherit; .fa { margin-left: $grid-gutter-width/2; diff --git a/addons/website/static/src/xml/website.pageProperties.xml b/addons/website/static/src/xml/website.pageProperties.xml index 8ad1d995ce5..348da5e9c17 100644 --- a/addons/website/static/src/xml/website.pageProperties.xml +++ b/addons/website/static/src/xml/website.pageProperties.xml @@ -49,7 +49,7 @@
- +
@@ -60,7 +60,7 @@
- +
diff --git a/addons/website/tests/__init__.py b/addons/website/tests/__init__.py index 1c57eeab793..85ecad1cba8 100644 --- a/addons/website/tests/__init__.py +++ b/addons/website/tests/__init__.py @@ -2,9 +2,11 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. from . import test_converter +from . import test_get_current_website from . import test_qweb from . import test_crawl from . import test_ui from . import test_views from . import test_menu from . import test_page +from . import test_website_reset_password diff --git a/addons/website/tests/test_get_current_website.py b/addons/website/tests/test_get_current_website.py new file mode 100644 index 00000000000..a71126cb484 --- /dev/null +++ b/addons/website/tests/test_get_current_website.py @@ -0,0 +1,114 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo.tests import tagged +from odoo.tests.common import TransactionCase + + +@tagged('post_install', '-at_install') +class TestGetCurrentWebsite(TransactionCase): + + def test_01_get_current_website_id(self): + """Make sure `_get_current_website_id works`.""" + + Website = self.env['website'] + + # clean initial state + website1 = Website.browse(1) + website1.domain = '' + website1.country_group_ids = False + + website2 = Website.browse(2) + website2.domain = '' + website2.country_group_ids = False + + country1 = self.env['res.country'].create({'name': "My Country 1"}) + country2 = self.env['res.country'].create({'name': "My Country 2"}) + country3 = self.env['res.country'].create({'name': "My Country 3"}) + country4 = self.env['res.country'].create({'name': "My Country 4"}) + country5 = self.env['res.country'].create({'name': "My Country 5"}) + + country_group_1_2 = self.env['res.country.group'].create({ + 'name': "My Country Group 1-2", + 'country_ids': [(6, 0, (country1 + country2 + country5).ids)], + }) + country_group_3 = self.env['res.country.group'].create({ + 'name': "My Country Group 3", + 'country_ids': [(6, 0, (country3 + country5).ids)], + }) + + # CASE: no domain, no country: get first + self.assertEqual(Website._get_current_website_id('', False), website1.id) + self.assertEqual(Website._get_current_website_id('', country3.id), website1.id) + + # CASE: no domain, given country: get by country + website1.country_group_ids = country_group_1_2 + website2.country_group_ids = country_group_3 + + self.assertEqual(Website._get_current_website_id('', country1.id), website1.id) + self.assertEqual(Website._get_current_website_id('', country2.id), website1.id) + self.assertEqual(Website._get_current_website_id('', country3.id), website2.id) + + # CASE: no domain, wrong country: get first + self.assertEqual(Website._get_current_website_id('', country4.id), website1.id) + + # CASE: no domain, multiple country: get first + self.assertEqual(Website._get_current_website_id('', country5.id), website1.id) + + # setup domain + website1.domain = 'my-site-1.fr' + website2.domain = 'https://my2ndsite.com:80' + + website1.country_group_ids = False + website2.country_group_ids = False + + # CASE: domain set: get matching domain + self.assertEqual(Website._get_current_website_id('my-site-1.fr', False), website1.id) + + # CASE: domain set: get matching domain (scheme and port supported) + self.assertEqual(Website._get_current_website_id('my-site-1.fr:8069', False), website1.id) + + self.assertEqual(Website._get_current_website_id('my2ndsite.com:80', False), website2.id) + self.assertEqual(Website._get_current_website_id('my2ndsite.com:8069', False), website2.id) + self.assertEqual(Website._get_current_website_id('my2ndsite.com', False), website2.id) + + # CASE: domain set, wrong domain: get first + self.assertEqual(Website._get_current_website_id('test.com', False), website1.id) + + # CASE: subdomain: not supported + self.assertEqual(Website._get_current_website_id('www.my2ndsite.com', False), website1.id) + + # CASE: domain set, given country: get by domain in priority + website1.country_group_ids = country_group_1_2 + website2.country_group_ids = country_group_3 + + self.assertEqual(Website._get_current_website_id('my2ndsite.com', country1.id), website2.id) + self.assertEqual(Website._get_current_website_id('my2ndsite.com', country2.id), website2.id) + self.assertEqual(Website._get_current_website_id('my-site-1.fr', country3.id), website1.id) + + # CASE: domain set, wrong country: get first for domain + self.assertEqual(Website._get_current_website_id('my2ndsite.com', country4.id), website2.id) + + # CASE: domain set, multiple country: get first for domain + website1.domain = website2.domain + self.assertEqual(Website._get_current_website_id('my2ndsite.com', country5.id), website1.id) + + # CASE: overlapping domain: get exact match + website1.domain = 'site-1.com' + website2.domain = 'even-better-site-1.com' + self.assertEqual(Website._get_current_website_id('site-1.com', False), website1.id) + self.assertEqual(Website._get_current_website_id('even-better-site-1.com', False), website2.id) + + # CASE: case insensitive + website1.domain = 'Site-1.com' + website2.domain = 'Even-Better-site-1.com' + self.assertEqual(Website._get_current_website_id('sitE-1.com', False), website1.id) + self.assertEqual(Website._get_current_website_id('even-beTTer-site-1.com', False), website2.id) + + # CASE: same domain, different port + website1.domain = 'site-1.com:80' + website2.domain = 'site-1.com:81' + self.assertEqual(Website._get_current_website_id('site-1.com:80', False), website1.id) + self.assertEqual(Website._get_current_website_id('site-1.com:81', False), website2.id) + self.assertEqual(Website._get_current_website_id('site-1.com:82', False), website1.id) + self.assertEqual(Website._get_current_website_id('site-1.com', False), website1.id) diff --git a/addons/website/tests/test_views.py b/addons/website/tests/test_views.py index 9ece6cf4c3d..0416bc72bf7 100644 --- a/addons/website/tests/test_views.py +++ b/addons/website/tests/test_views.py @@ -159,6 +159,12 @@ class TestViewSaving(common.TransactionCase): h.LI(h.SPAN("+12 3456789", attrs(model='res.company', id=1, field='phone', expression="edmund", type='char'))), ) ), encoding='unicode') + + self.view_id.with_context(website_id=1).save(value=replacement, xpath='/div/div[2]') + self.assertFalse(imd.noupdate, "view's xml_id shouldn't be set to 'noupdate' in a website context as `save` method will COW") + # remove newly created COW view so next `save()`` wont be redirected to COW view + self.env['website'].with_context(website_id=1).viewref(self.view_id.key).unlink() + self.view_id.save(value=replacement, xpath='/div/div[2]') # the xml_id of the view should be flagged as 'noupdate' @@ -622,6 +628,76 @@ class TestCowViewSaving(common.TransactionCase): views = View.with_context(website_id=1).get_related_views('B') self.assertEqual(views.mapped('key'), ['B', 'I', 'II'], "Should only return the specific tree") + def test_get_related_views_tree_recursive_t_call_and_inherit_inactive(self): + """ If a view A was doing a t-call on a view B and view B had view C as child. + And view A had view D as child. + And view D also t-call view B (that as mentionned above has view C as child). + And view D was inactive (`d` in bellow schema). + + Then COWing C to set it as inactive would make `get_related_views()` on A to return + both generic active C and COW inactive C. + (Typically the case for Customize show on /shop for Wishlist, compare..) + See commit message for detailed explanation. + """ + # A -> B + # | ^ \ + # | | C + # d ___| + + View = self.env['ir.ui.view'] + Website = self.env['website'] + + products = View.create({ + 'name': 'Products', + 'type': 'qweb', + 'key': '_website_sale.products', + 'arch': ''' +
+ +
+ ''', + }) + + products_item = View.create({ + 'name': 'Products item', + 'type': 'qweb', + 'key': '_website_sale.products_item', + 'arch': ''' +
+ ''', + }) + + add_to_wishlist = View.create({ + 'name': 'Wishlist', + 'active': True, + 'customize_show': True, + 'inherit_id': products_item.id, + 'key': '_website_sale_wishlist.add_to_wishlist', + 'arch': ''' + + ''', + }) + + products_list_view = View.create({ + 'name': 'List View', + 'active': False, # <- That's the reason of why this behavior needed a fix + 'customize_show': True, + 'inherit_id': products.id, + 'key': '_website_sale.products_list_view', + 'arch': ''' +
+ +
+ ''', + }) + + views = View.with_context(website_id=1).get_related_views('_website_sale.products') + self.assertEqual(views, products + products_item + add_to_wishlist + products_list_view, "The four views should be returned.") + add_to_wishlist.with_context(website_id=1).write({'active': False}) # Trigger cow on hierarchy + add_to_wishlist_cow = Website.with_context(website_id=1).viewref(add_to_wishlist.key) + views = View.with_context(website_id=1).get_related_views('_website_sale.products') + self.assertEqual(views, products + products_item + add_to_wishlist_cow + products_list_view, "The generic wishlist view should have been replaced by the COW one.") + def test_cow_inherit_children_order(self): """ COW method should loop on inherit_children_ids in correct order when copying them on the new specific tree. diff --git a/addons/website/tests/test_website_reset_password.py b/addons/website/tests/test_website_reset_password.py new file mode 100644 index 00000000000..b9fe135d35f --- /dev/null +++ b/addons/website/tests/test_website_reset_password.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +try: + from unittest.mock import patch +except ImportError: + from mock import patch + +import odoo +from odoo.tests import tagged +from odoo.tests.common import HttpCase + + +@tagged('post_install', '-at_install') +class TestWebsiteResetPassword(HttpCase): + + def test_01_website_reset_password_tour(self): + """The goal of this test is to make sure the reset password works.""" + + # We override unlink because we don't want the email to be auto deleted + # if the send works. + MailMail = odoo.addons.mail.models.mail_mail.MailMail + + # We override send_mail because in HttpCase on runbot we don't have an + # SMTP server, so if force_send is set, the test is going to fail. + MailTemplate = odoo.addons.mail.models.mail_template.MailTemplate + original_send_mail = MailTemplate.send_mail + + def my_send_mail(*args, **kwargs): + kwargs.update(force_send=False) + return original_send_mail(*args, **kwargs) + + with patch.object(MailMail, 'unlink', lambda self: None), patch.object(MailTemplate, 'send_mail', my_send_mail): + self.browser_js("/", "odoo.__DEBUG__.services['web_tour.tour'].run('website_reset_password')", "odoo.__DEBUG__.services['web_tour.tour'].tours.website_reset_password.ready", login="admin") diff --git a/addons/website/views/website_templates.xml b/addons/website/views/website_templates.xml index 8809cd292d4..3ec2fe607ef 100644 --- a/addons/website/views/website_templates.xml +++ b/addons/website/views/website_templates.xml @@ -17,6 +17,12 @@ + + diff --git a/addons/website_rating/static/src/js/website_mail.js b/addons/website_rating/static/src/js/website_mail.js index 8f21f740c98..6fd9a305e95 100644 --- a/addons/website_rating/static/src/js/website_mail.js +++ b/addons/website_rating/static/src/js/website_mail.js @@ -33,6 +33,11 @@ odoo.define('website_rating.thread', function(require) { init: function(parent, options){ this._super.apply(this, arguments); + // apply ratio to default rating value + if (options.default_rating_value) { + options.default_rating_value = parseFloat(options.default_rating_value) / STAR_RATING_RATIO; + } + // default options this.options = _.defaults(this.options, { 'default_message': false, @@ -50,7 +55,7 @@ odoo.define('website_rating.thread', function(require) { '5': _t("I love it"), }; this.user_click = false; // user has click or not - this.set("star_value", this.options.rating_default_value); + this.set("star_value", this.options.default_rating_value); this.on("change:star_value", this, this._onChangeStarValue); }, start: function(){ @@ -59,7 +64,7 @@ odoo.define('website_rating.thread', function(require) { // rating stars self.$input = self.$('input[name="rating_value"]'); self.$star_list = self.$('.stars').find('i'); - self.set("star_value", self.options.rating_default_value); // set the default value to trigger the display of star widget + self.set("star_value", self.options.default_rating_value); // set the default value to trigger the display of star widget }); }, @@ -265,7 +270,7 @@ odoo.define('website_rating.thread', function(require) { _onChangeRatingDomain: function(){ var domain = []; if(this.get('rating_value')){ - domain = [['rating_value', '=', this.get('rating_value')]]; + domain = [['rating_value', '=', this.get('rating_value') * STAR_RATING_RATIO]]; } this._changeCurrentPage(1, domain); }, diff --git a/addons/website_rating/static/src/xml/website_mail.xml b/addons/website_rating/static/src/xml/website_mail.xml index 11534a768c2..d4e8dd83218 100644 --- a/addons/website_rating/static/src/xml/website_mail.xml +++ b/addons/website_rating/static/src/xml/website_mail.xml @@ -79,7 +79,7 @@
- +
diff --git a/addons/website_rating/views/website_rating_templates.xml b/addons/website_rating/views/website_rating_templates.xml index 32dc3d3c573..8ce20495b32 100644 --- a/addons/website_rating/views/website_rating_templates.xml +++ b/addons/website_rating/views/website_rating_templates.xml @@ -18,7 +18,7 @@ Rating widget static: show 5 stars (full or empty) regarding the given rating_avg and rating_count -->