diff --git a/.tx/config b/.tx/config new file mode 100644 index 00000000000..23e76595a0f --- /dev/null +++ b/.tx/config @@ -0,0 +1,1133 @@ +[main] +host = https://www.transifex.com +type = PO + +[odoo-9.base] +file_filter = openerp/addons/base/i18n/.po +source_file = openerp/addons/base/i18n/base.pot +source_lang = en + +[odoo-9.account] +file_filter = addons/account/i18n/.po +source_file = addons/account/i18n/account.pot +source_lang = en + +[odoo-9.account_accountant] +file_filter = addons/account_accountant/i18n/.po +source_file = addons/account_accountant/i18n/account_accountant.pot +source_lang = en + +[odoo-9.account_analytic_analysis] +file_filter = addons/account_analytic_analysis/i18n/.po +source_file = addons/account_analytic_analysis/i18n/account_analytic_analysis.pot +source_lang = en + +[odoo-9.account_analytic_default] +file_filter = addons/account_analytic_default/i18n/.po +source_file = addons/account_analytic_default/i18n/account_analytic_default.pot +source_lang = en + +[odoo-9.account_analytic_plans] +file_filter = addons/account_analytic_plans/i18n/.po +source_file = addons/account_analytic_plans/i18n/account_analytic_plans.pot +source_lang = en + +[odoo-9.account_anglo_saxon] +file_filter = addons/account_anglo_saxon/i18n/.po +source_file = addons/account_anglo_saxon/i18n/account_anglo_saxon.pot +source_lang = en + +[odoo-9.account_asset] +file_filter = addons/account_asset/i18n/.po +source_file = addons/account_asset/i18n/account_asset.pot +source_lang = en + +[odoo-9.account_bank_statement_extensions] +file_filter = addons/account_bank_statement_extensions/i18n/.po +source_file = addons/account_bank_statement_extensions/i18n/account_bank_statement_extensions.pot +source_lang = en + +[odoo-9.account_budget] +file_filter = addons/account_budget/i18n/.po +source_file = addons/account_budget/i18n/account_budget.pot +source_lang = en + +[odoo-9.account_cancel] +file_filter = addons/account_cancel/i18n/.po +source_file = addons/account_cancel/i18n/account_cancel.pot +source_lang = en + +[odoo-9.account_chart] +file_filter = addons/account_chart/i18n/.po +source_file = addons/account_chart/i18n/account_chart.pot +source_lang = en + +[odoo-9.account_check_writing] +file_filter = addons/account_check_writing/i18n/.po +source_file = addons/account_check_writing/i18n/account_check_writing.pot +source_lang = en + +[odoo-9.account_followup] +file_filter = addons/account_followup/i18n/.po +source_file = addons/account_followup/i18n/account_followup.pot +source_lang = en + +[odoo-9.account_payment] +file_filter = addons/account_payment/i18n/.po +source_file = addons/account_payment/i18n/account_payment.pot +source_lang = en + +[odoo-9.account_sequence] +file_filter = addons/account_sequence/i18n/.po +source_file = addons/account_sequence/i18n/account_sequence.pot +source_lang = en + +[odoo-9.account_test] +file_filter = addons/account_test/i18n/.po +source_file = addons/account_test/i18n/account_test.pot +source_lang = en + +[odoo-9.account_voucher] +file_filter = addons/account_voucher/i18n/.po +source_file = addons/account_voucher/i18n/account_voucher.pot +source_lang = en + +[odoo-9.analytic] +file_filter = addons/analytic/i18n/.po +source_file = addons/analytic/i18n/analytic.pot +source_lang = en + +[odoo-9.analytic_contract_hr_expense] +file_filter = addons/analytic_contract_hr_expense/i18n/.po +source_file = addons/analytic_contract_hr_expense/i18n/analytic_contract_hr_expense.pot +source_lang = en + +[odoo-9.analytic_user_function] +file_filter = addons/analytic_user_function/i18n/.po +source_file = addons/analytic_user_function/i18n/analytic_user_function.pot +source_lang = en + +[odoo-9.anonymization] +file_filter = addons/anonymization/i18n/.po +source_file = addons/anonymization/i18n/anonymization.pot +source_lang = en + +[odoo-9.association] +file_filter = addons/association/i18n/.po +source_file = addons/association/i18n/association.pot +source_lang = en + +[odoo-9.auth_crypt] +file_filter = addons/auth_crypt/i18n/.po +source_file = addons/auth_crypt/i18n/auth_crypt.pot +source_lang = en + +[odoo-9.auth_ldap] +file_filter = addons/auth_ldap/i18n/.po +source_file = addons/auth_ldap/i18n/auth_ldap.pot +source_lang = en + +[odoo-9.auth_oauth] +file_filter = addons/auth_oauth/i18n/.po +source_file = addons/auth_oauth/i18n/auth_oauth.pot +source_lang = en + +[odoo-9.auth_openid] +file_filter = addons/auth_openid/i18n/.po +source_file = addons/auth_openid/i18n/auth_openid.pot +source_lang = en + +[odoo-9.auth_signup] +file_filter = addons/auth_signup/i18n/.po +source_file = addons/auth_signup/i18n/auth_signup.pot +source_lang = en + +[odoo-9.base_action_rule] +file_filter = addons/base_action_rule/i18n/.po +source_file = addons/base_action_rule/i18n/base_action_rule.pot +source_lang = en + +[odoo-9.base_gengo] +file_filter = addons/base_gengo/i18n/.po +source_file = addons/base_gengo/i18n/base_gengo.pot +source_lang = en + +[odoo-9.base_geolocalize] +file_filter = addons/base_geolocalize/i18n/.po +source_file = addons/base_geolocalize/i18n/base_geolocalize.pot +source_lang = en + +[odoo-9.base_iban] +file_filter = addons/base_iban/i18n/.po +source_file = addons/base_iban/i18n/base_iban.pot +source_lang = en + +[odoo-9.base_import] +file_filter = addons/base_import/i18n/.po +source_file = addons/base_import/i18n/base_import.pot +source_lang = en + +[odoo-9.base_import_module] +file_filter = addons/base_import_module/i18n/.po +source_file = addons/base_import_module/i18n/base_import_module.pot +source_lang = en + +[odoo-9.base_report_designer] +file_filter = addons/base_report_designer/i18n/.po +source_file = addons/base_report_designer/i18n/base_report_designer.pot +source_lang = en + +[odoo-9.base_setup] +file_filter = addons/base_setup/i18n/.po +source_file = addons/base_setup/i18n/base_setup.pot +source_lang = en + +[odoo-9.base_vat] +file_filter = addons/base_vat/i18n/.po +source_file = addons/base_vat/i18n/base_vat.pot +source_lang = en + +[odoo-9.board] +file_filter = addons/board/i18n/.po +source_file = addons/board/i18n/board.pot +source_lang = en + +[odoo-9.bus] +file_filter = addons/bus/i18n/.po +source_file = addons/bus/i18n/bus.pot +source_lang = en + +[odoo-9.calendar] +file_filter = addons/calendar/i18n/.po +source_file = addons/calendar/i18n/calendar.pot +source_lang = en + +[odoo-9.claim_from_delivery] +file_filter = addons/claim_from_delivery/i18n/.po +source_file = addons/claim_from_delivery/i18n/claim_from_delivery.pot +source_lang = en + +[odoo-9.contacts] +file_filter = addons/contacts/i18n/.po +source_file = addons/contacts/i18n/contacts.pot +source_lang = en + +[odoo-9.crm] +file_filter = addons/crm/i18n/.po +source_file = addons/crm/i18n/crm.pot +source_lang = en + +[odoo-9.crm_claim] +file_filter = addons/crm_claim/i18n/.po +source_file = addons/crm_claim/i18n/crm_claim.pot +source_lang = en + +[odoo-9.crm_helpdesk] +file_filter = addons/crm_helpdesk/i18n/.po +source_file = addons/crm_helpdesk/i18n/crm_helpdesk.pot +source_lang = en + +[odoo-9.crm_mass_mailing] +file_filter = addons/crm_mass_mailing/i18n/.po +source_file = addons/crm_mass_mailing/i18n/crm_mass_mailing.pot +source_lang = en + +[odoo-9.crm_partner_assign] +file_filter = addons/crm_partner_assign/i18n/.po +source_file = addons/crm_partner_assign/i18n/crm_partner_assign.pot +source_lang = en + +[odoo-9.crm_profiling] +file_filter = addons/crm_profiling/i18n/.po +source_file = addons/crm_profiling/i18n/crm_profiling.pot +source_lang = en + +[odoo-9.crm_project_issue] +file_filter = addons/crm_project_issue/i18n/.po +source_file = addons/crm_project_issue/i18n/crm_project_issue.pot +source_lang = en + +[odoo-9.decimal_precision] +file_filter = addons/decimal_precision/i18n/.po +source_file = addons/decimal_precision/i18n/decimal_precision.pot +source_lang = en + +[odoo-9.delivery] +file_filter = addons/delivery/i18n/.po +source_file = addons/delivery/i18n/delivery.pot +source_lang = en + +[odoo-9.document] +file_filter = addons/document/i18n/.po +source_file = addons/document/i18n/document.pot +source_lang = en + +[odoo-9.edi] +file_filter = addons/edi/i18n/.po +source_file = addons/edi/i18n/edi.pot +source_lang = en + +[odoo-9.email_template] +file_filter = addons/email_template/i18n/.po +source_file = addons/email_template/i18n/email_template.pot +source_lang = en + +[odoo-9.event] +file_filter = addons/event/i18n/.po +source_file = addons/event/i18n/event.pot +source_lang = en + +[odoo-9.event_sale] +file_filter = addons/event_sale/i18n/.po +source_file = addons/event_sale/i18n/event_sale.pot +source_lang = en + +[odoo-9.fetchmail] +file_filter = addons/fetchmail/i18n/.po +source_file = addons/fetchmail/i18n/fetchmail.pot +source_lang = en + +[odoo-9.fleet] +file_filter = addons/fleet/i18n/.po +source_file = addons/fleet/i18n/fleet.pot +source_lang = en + +[odoo-9.gamification] +file_filter = addons/gamification/i18n/.po +source_file = addons/gamification/i18n/gamification.pot +source_lang = en + +[odoo-9.gamification_sale_crm] +file_filter = addons/gamification_sale_crm/i18n/.po +source_file = addons/gamification_sale_crm/i18n/gamification_sale_crm.pot +source_lang = en + +[odoo-9.google_account] +file_filter = addons/google_account/i18n/.po +source_file = addons/google_account/i18n/google_account.pot +source_lang = en + +[odoo-9.google_calendar] +file_filter = addons/google_calendar/i18n/.po +source_file = addons/google_calendar/i18n/google_calendar.pot +source_lang = en + +[odoo-9.google_drive] +file_filter = addons/google_drive/i18n/.po +source_file = addons/google_drive/i18n/google_drive.pot +source_lang = en + +[odoo-9.google_spreadsheet] +file_filter = addons/google_spreadsheet/i18n/.po +source_file = addons/google_spreadsheet/i18n/google_spreadsheet.pot +source_lang = en + +[odoo-9.hr] +file_filter = addons/hr/i18n/.po +source_file = addons/hr/i18n/hr.pot +source_lang = en + +[odoo-9.hr_applicant_document] +file_filter = addons/hr_applicant_document/i18n/.po +source_file = addons/hr_applicant_document/i18n/hr_applicant_document.pot +source_lang = en + +[odoo-9.hr_attendance] +file_filter = addons/hr_attendance/i18n/.po +source_file = addons/hr_attendance/i18n/hr_attendance.pot +source_lang = en + +[odoo-9.hr_contract] +file_filter = addons/hr_contract/i18n/.po +source_file = addons/hr_contract/i18n/hr_contract.pot +source_lang = en + +[odoo-9.hr_evaluation] +file_filter = addons/hr_evaluation/i18n/.po +source_file = addons/hr_evaluation/i18n/hr_evaluation.pot +source_lang = en + +[odoo-9.hr_expense] +file_filter = addons/hr_expense/i18n/.po +source_file = addons/hr_expense/i18n/hr_expense.pot +source_lang = en + +[odoo-9.hr_gamification] +file_filter = addons/hr_gamification/i18n/.po +source_file = addons/hr_gamification/i18n/hr_gamification.pot +source_lang = en + +[odoo-9.hr_holidays] +file_filter = addons/hr_holidays/i18n/.po +source_file = addons/hr_holidays/i18n/hr_holidays.pot +source_lang = en + +[odoo-9.hr_payroll] +file_filter = addons/hr_payroll/i18n/.po +source_file = addons/hr_payroll/i18n/hr_payroll.pot +source_lang = en + +[odoo-9.hr_payroll_account] +file_filter = addons/hr_payroll_account/i18n/.po +source_file = addons/hr_payroll_account/i18n/hr_payroll_account.pot +source_lang = en + +[odoo-9.hr_recruitment] +file_filter = addons/hr_recruitment/i18n/.po +source_file = addons/hr_recruitment/i18n/hr_recruitment.pot +source_lang = en + +[odoo-9.hr_timesheet] +file_filter = addons/hr_timesheet/i18n/.po +source_file = addons/hr_timesheet/i18n/hr_timesheet.pot +source_lang = en + +[odoo-9.hr_timesheet_invoice] +file_filter = addons/hr_timesheet_invoice/i18n/.po +source_file = addons/hr_timesheet_invoice/i18n/hr_timesheet_invoice.pot +source_lang = en + +[odoo-9.hr_timesheet_sheet] +file_filter = addons/hr_timesheet_sheet/i18n/.po +source_file = addons/hr_timesheet_sheet/i18n/hr_timesheet_sheet.pot +source_lang = en + +[odoo-9.im_chat] +file_filter = addons/im_chat/i18n/.po +source_file = addons/im_chat/i18n/im_chat.pot +source_lang = en + +[odoo-9.im_livechat] +file_filter = addons/im_livechat/i18n/.po +source_file = addons/im_livechat/i18n/im_livechat.pot +source_lang = en + +[odoo-9.im_odoo_support] +file_filter = addons/im_odoo_support/i18n/.po +source_file = addons/im_odoo_support/i18n/im_odoo_support.pot +source_lang = en + +[odoo-9.knowledge] +file_filter = addons/knowledge/i18n/.po +source_file = addons/knowledge/i18n/knowledge.pot +source_lang = en + +[odoo-9.l10n_ar] +file_filter = addons/l10n_ar/i18n/.po +source_file = addons/l10n_ar/i18n/l10n_ar.pot +source_lang = en + +[odoo-9.l10n_be] +file_filter = addons/l10n_be/i18n/.po +source_file = addons/l10n_be/i18n/l10n_be.pot +source_lang = en + +[odoo-9.l10n_be_coda] +file_filter = addons/l10n_be_coda/i18n/.po +source_file = addons/l10n_be_coda/i18n/l10n_be_coda.pot +source_lang = en + +[odoo-9.l10n_be_hr_payroll] +file_filter = addons/l10n_be_hr_payroll/i18n/.po +source_file = addons/l10n_be_hr_payroll/i18n/l10n_be_hr_payroll.pot +source_lang = en + +[odoo-9.l10n_be_intrastat] +file_filter = addons/l10n_be_intrastat/i18n/.po +source_file = addons/l10n_be_intrastat/i18n/l10n_be_intrastat.pot +source_lang = en + +[odoo-9.l10n_be_invoice_bba] +file_filter = addons/l10n_be_invoice_bba/i18n/.po +source_file = addons/l10n_be_invoice_bba/i18n/l10n_be_invoice_bba.pot +source_lang = en + +[odoo-9.l10n_bo] +file_filter = addons/l10n_bo/i18n/.po +source_file = addons/l10n_bo/i18n/l10n_bo.pot +source_lang = en + +[odoo-9.l10n_br] +file_filter = addons/l10n_br/i18n/.po +source_file = addons/l10n_br/i18n/l10n_br.pot +source_lang = en + +[odoo-9.l10n_cl] +file_filter = addons/l10n_cl/i18n/.po +source_file = addons/l10n_cl/i18n/l10n_cl.pot +source_lang = en + +[odoo-9.l10n_cr] +file_filter = addons/l10n_cr/i18n/.po +source_file = addons/l10n_cr/i18n/l10n_cr.pot +source_lang = en + +[odoo-9.l10n_de] +file_filter = addons/l10n_de/i18n/.po +source_file = addons/l10n_de/i18n/l10n_de.pot +source_lang = en + +[odoo-9.l10n_ec] +file_filter = addons/l10n_ec/i18n/.po +source_file = addons/l10n_ec/i18n/l10n_ec.pot +source_lang = en + +[odoo-9.l10n_es] +file_filter = addons/l10n_es/i18n/.po +source_file = addons/l10n_es/i18n/l10n_es.pot +source_lang = en + +[odoo-9.l10n_eu_service] +file_filter = addons/l10n_eu_service/i18n/.po +source_file = addons/l10n_eu_service/i18n/l10n_eu_service.pot +source_lang = en + +[odoo-9.l10n_fr] +file_filter = addons/l10n_fr/i18n/.po +source_file = addons/l10n_fr/i18n/l10n_fr.pot +source_lang = en + +[odoo-9.l10n_fr_rib] +file_filter = addons/l10n_fr_rib/i18n/.po +source_file = addons/l10n_fr_rib/i18n/l10n_fr_rib.pot +source_lang = en + +[odoo-9.l10n_gr] +file_filter = addons/l10n_gr/i18n/.po +source_file = addons/l10n_gr/i18n/l10n_gr.pot +source_lang = en + +[odoo-9.l10n_gt] +file_filter = addons/l10n_gt/i18n/.po +source_file = addons/l10n_gt/i18n/l10n_gt.pot +source_lang = en + +[odoo-9.l10n_hn] +file_filter = addons/l10n_hn/i18n/.po +source_file = addons/l10n_hn/i18n/l10n_hn.pot +source_lang = en + +[odoo-9.l10n_in] +file_filter = addons/l10n_in/i18n/.po +source_file = addons/l10n_in/i18n/l10n_in.pot +source_lang = en + +[odoo-9.l10n_in_hr_payroll] +file_filter = addons/l10n_in_hr_payroll/i18n/.po +source_file = addons/l10n_in_hr_payroll/i18n/l10n_in_hr_payroll.pot +source_lang = en + +[odoo-9.l10n_it] +file_filter = addons/l10n_it/i18n/.po +source_file = addons/l10n_it/i18n/l10n_it.pot +source_lang = en + +[odoo-9.l10n_lu] +file_filter = addons/l10n_lu/i18n/.po +source_file = addons/l10n_lu/i18n/l10n_lu.pot +source_lang = en + +[odoo-9.l10n_ma] +file_filter = addons/l10n_ma/i18n/.po +source_file = addons/l10n_ma/i18n/l10n_ma.pot +source_lang = en + +[odoo-9.l10n_multilang] +file_filter = addons/l10n_multilang/i18n/.po +source_file = addons/l10n_multilang/i18n/l10n_multilang.pot +source_lang = en + +[odoo-9.l10n_nl] +file_filter = addons/l10n_nl/i18n/.po +source_file = addons/l10n_nl/i18n/l10n_nl.pot +source_lang = en + +[odoo-9.l10n_pe] +file_filter = addons/l10n_pe/i18n/.po +source_file = addons/l10n_pe/i18n/l10n_pe.pot +source_lang = en + +[odoo-9.l10n_pl] +file_filter = addons/l10n_pl/i18n/.po +source_file = addons/l10n_pl/i18n/l10n_pl.pot +source_lang = en + +[odoo-9.l10n_syscohada] +file_filter = addons/l10n_syscohada/i18n/.po +source_file = addons/l10n_syscohada/i18n/l10n_syscohada.pot +source_lang = en + +[odoo-9.l10n_th] +file_filter = addons/l10n_th/i18n/.po +source_file = addons/l10n_th/i18n/l10n_th.pot +source_lang = en + +[odoo-9.l10n_uk] +file_filter = addons/l10n_uk/i18n/.po +source_file = addons/l10n_uk/i18n/l10n_uk.pot +source_lang = en + +[odoo-9.l10n_ve] +file_filter = addons/l10n_ve/i18n/.po +source_file = addons/l10n_ve/i18n/l10n_ve.pot +source_lang = en + +[odoo-9.lunch] +file_filter = addons/lunch/i18n/.po +source_file = addons/lunch/i18n/lunch.pot +source_lang = en + +[odoo-9.mail] +file_filter = addons/mail/i18n/.po +source_file = addons/mail/i18n/mail.pot +source_lang = en + +[odoo-9.marketing] +file_filter = addons/marketing/i18n/.po +source_file = addons/marketing/i18n/marketing.pot +source_lang = en + +[odoo-9.marketing_campaign] +file_filter = addons/marketing_campaign/i18n/.po +source_file = addons/marketing_campaign/i18n/marketing_campaign.pot +source_lang = en + +[odoo-9.marketing_campaign_crm_demo] +file_filter = addons/marketing_campaign_crm_demo/i18n/.po +source_file = addons/marketing_campaign_crm_demo/i18n/marketing_campaign_crm_demo.pot +source_lang = en + +[odoo-9.marketing_crm] +file_filter = addons/marketing_crm/i18n/.po +source_file = addons/marketing_crm/i18n/marketing_crm.pot +source_lang = en + +[odoo-9.mass_mailing] +file_filter = addons/mass_mailing/i18n/.po +source_file = addons/mass_mailing/i18n/mass_mailing.pot +source_lang = en + +[odoo-9.membership] +file_filter = addons/membership/i18n/.po +source_file = addons/membership/i18n/membership.pot +source_lang = en + +[odoo-9.mrp] +file_filter = addons/mrp/i18n/.po +source_file = addons/mrp/i18n/mrp.pot +source_lang = en + +[odoo-9.mrp_byproduct] +file_filter = addons/mrp_byproduct/i18n/.po +source_file = addons/mrp_byproduct/i18n/mrp_byproduct.pot +source_lang = en + +[odoo-9.mrp_operations] +file_filter = addons/mrp_operations/i18n/.po +source_file = addons/mrp_operations/i18n/mrp_operations.pot +source_lang = en + +[odoo-9.mrp_repair] +file_filter = addons/mrp_repair/i18n/.po +source_file = addons/mrp_repair/i18n/mrp_repair.pot +source_lang = en + +[odoo-9.multi_company] +file_filter = addons/multi_company/i18n/.po +source_file = addons/multi_company/i18n/multi_company.pot +source_lang = en + +[odoo-9.note] +file_filter = addons/note/i18n/.po +source_file = addons/note/i18n/note.pot +source_lang = en + +[odoo-9.note_pad] +file_filter = addons/note_pad/i18n/.po +source_file = addons/note_pad/i18n/note_pad.pot +source_lang = en + +[odoo-9.pad] +file_filter = addons/pad/i18n/.po +source_file = addons/pad/i18n/pad.pot +source_lang = en + +[odoo-9.pad_project] +file_filter = addons/pad_project/i18n/.po +source_file = addons/pad_project/i18n/pad_project.pot +source_lang = en + +[odoo-9.payment] +file_filter = addons/payment/i18n/.po +source_file = addons/payment/i18n/payment.pot +source_lang = en + +[odoo-9.payment_adyen] +file_filter = addons/payment_adyen/i18n/.po +source_file = addons/payment_adyen/i18n/payment_adyen.pot +source_lang = en + +[odoo-9.payment_buckaroo] +file_filter = addons/payment_buckaroo/i18n/.po +source_file = addons/payment_buckaroo/i18n/payment_buckaroo.pot +source_lang = en + +[odoo-9.payment_ogone] +file_filter = addons/payment_ogone/i18n/.po +source_file = addons/payment_ogone/i18n/payment_ogone.pot +source_lang = en + +[odoo-9.payment_paypal] +file_filter = addons/payment_paypal/i18n/.po +source_file = addons/payment_paypal/i18n/payment_paypal.pot +source_lang = en + +[odoo-9.payment_transfer] +file_filter = addons/payment_transfer/i18n/.po +source_file = addons/payment_transfer/i18n/payment_transfer.pot +source_lang = en + +[odoo-9.point_of_sale] +file_filter = addons/point_of_sale/i18n/.po +source_file = addons/point_of_sale/i18n/point_of_sale.pot +source_lang = en + +[odoo-9.portal] +file_filter = addons/portal/i18n/.po +source_file = addons/portal/i18n/portal.pot +source_lang = en + +[odoo-9.portal_claim] +file_filter = addons/portal_claim/i18n/.po +source_file = addons/portal_claim/i18n/portal_claim.pot +source_lang = en + +[odoo-9.portal_project] +file_filter = addons/portal_project/i18n/.po +source_file = addons/portal_project/i18n/portal_project.pot +source_lang = en + +[odoo-9.portal_project_issue] +file_filter = addons/portal_project_issue/i18n/.po +source_file = addons/portal_project_issue/i18n/portal_project_issue.pot +source_lang = en + +[odoo-9.portal_sale] +file_filter = addons/portal_sale/i18n/.po +source_file = addons/portal_sale/i18n/portal_sale.pot +source_lang = en + +[odoo-9.pos_discount] +file_filter = addons/pos_discount/i18n/.po +source_file = addons/pos_discount/i18n/pos_discount.pot +source_lang = en + +[odoo-9.pos_restaurant] +file_filter = addons/pos_restaurant/i18n/.po +source_file = addons/pos_restaurant/i18n/pos_restaurant.pot +source_lang = en + +[odoo-9.procurement] +file_filter = addons/procurement/i18n/.po +source_file = addons/procurement/i18n/procurement.pot +source_lang = en + +[odoo-9.procurement_jit] +file_filter = addons/procurement_jit/i18n/.po +source_file = addons/procurement_jit/i18n/procurement_jit.pot +source_lang = en + +[odoo-9.product] +file_filter = addons/product/i18n/.po +source_file = addons/product/i18n/product.pot +source_lang = en + +[odoo-9.product_email_template] +file_filter = addons/product_email_template/i18n/.po +source_file = addons/product_email_template/i18n/product_email_template.pot +source_lang = en + +[odoo-9.product_expiry] +file_filter = addons/product_expiry/i18n/.po +source_file = addons/product_expiry/i18n/product_expiry.pot +source_lang = en + +[odoo-9.product_extended] +file_filter = addons/product_extended/i18n/.po +source_file = addons/product_extended/i18n/product_extended.pot +source_lang = en + +[odoo-9.product_margin] +file_filter = addons/product_margin/i18n/.po +source_file = addons/product_margin/i18n/product_margin.pot +source_lang = en + +[odoo-9.product_visible_discount] +file_filter = addons/product_visible_discount/i18n/.po +source_file = addons/product_visible_discount/i18n/product_visible_discount.pot +source_lang = en + +[odoo-9.project] +file_filter = addons/project/i18n/.po +source_file = addons/project/i18n/project.pot +source_lang = en + +[odoo-9.project_issue] +file_filter = addons/project_issue/i18n/.po +source_file = addons/project_issue/i18n/project_issue.pot +source_lang = en + +[odoo-9.project_issue_sheet] +file_filter = addons/project_issue_sheet/i18n/.po +source_file = addons/project_issue_sheet/i18n/project_issue_sheet.pot +source_lang = en + +[odoo-9.project_timesheet] +file_filter = addons/project_timesheet/i18n/.po +source_file = addons/project_timesheet/i18n/project_timesheet.pot +source_lang = en + +[odoo-9.purchase] +file_filter = addons/purchase/i18n/.po +source_file = addons/purchase/i18n/purchase.pot +source_lang = en + +[odoo-9.purchase_analytic_plans] +file_filter = addons/purchase_analytic_plans/i18n/.po +source_file = addons/purchase_analytic_plans/i18n/purchase_analytic_plans.pot +source_lang = en + +[odoo-9.purchase_double_validation] +file_filter = addons/purchase_double_validation/i18n/.po +source_file = addons/purchase_double_validation/i18n/purchase_double_validation.pot +source_lang = en + +[odoo-9.purchase_requisition] +file_filter = addons/purchase_requisition/i18n/.po +source_file = addons/purchase_requisition/i18n/purchase_requisition.pot +source_lang = en + +[odoo-9.report] +file_filter = addons/report/i18n/.po +source_file = addons/report/i18n/report.pot +source_lang = en + +[odoo-9.report_intrastat] +file_filter = addons/report_intrastat/i18n/.po +source_file = addons/report_intrastat/i18n/report_intrastat.pot +source_lang = en + +[odoo-9.report_webkit] +file_filter = addons/report_webkit/i18n/.po +source_file = addons/report_webkit/i18n/report_webkit.pot +source_lang = en + +[odoo-9.resource] +file_filter = addons/resource/i18n/.po +source_file = addons/resource/i18n/resource.pot +source_lang = en + +[odoo-9.sale] +file_filter = addons/sale/i18n/.po +source_file = addons/sale/i18n/sale.pot +source_lang = en + +[odoo-9.sale_analytic_plans] +file_filter = addons/sale_analytic_plans/i18n/.po +source_file = addons/sale_analytic_plans/i18n/sale_analytic_plans.pot +source_lang = en + +[odoo-9.sale_crm] +file_filter = addons/sale_crm/i18n/.po +source_file = addons/sale_crm/i18n/sale_crm.pot +source_lang = en + +[odoo-9.sale_journal] +file_filter = addons/sale_journal/i18n/.po +source_file = addons/sale_journal/i18n/sale_journal.pot +source_lang = en + +[odoo-9.sale_layout] +file_filter = addons/sale_layout/i18n/.po +source_file = addons/sale_layout/i18n/sale_layout.pot +source_lang = en + +[odoo-9.sale_margin] +file_filter = addons/sale_margin/i18n/.po +source_file = addons/sale_margin/i18n/sale_margin.pot +source_lang = en + +[odoo-9.sale_mrp] +file_filter = addons/sale_mrp/i18n/.po +source_file = addons/sale_mrp/i18n/sale_mrp.pot +source_lang = en + +[odoo-9.sale_order_dates] +file_filter = addons/sale_order_dates/i18n/.po +source_file = addons/sale_order_dates/i18n/sale_order_dates.pot +source_lang = en + +[odoo-9.sale_service] +file_filter = addons/sale_service/i18n/.po +source_file = addons/sale_service/i18n/sale_service.pot +source_lang = en + +[odoo-9.sales_team] +file_filter = addons/sales_team/i18n/.po +source_file = addons/sales_team/i18n/sales_team.pot +source_lang = en + +[odoo-9.sale_stock] +file_filter = addons/sale_stock/i18n/.po +source_file = addons/sale_stock/i18n/sale_stock.pot +source_lang = en + +[odoo-9.share] +file_filter = addons/share/i18n/.po +source_file = addons/share/i18n/share.pot +source_lang = en + +[odoo-9.stock] +file_filter = addons/stock/i18n/.po +source_file = addons/stock/i18n/stock.pot +source_lang = en + +[odoo-9.stock_account] +file_filter = addons/stock_account/i18n/.po +source_file = addons/stock_account/i18n/stock_account.pot +source_lang = en + +[odoo-9.stock_dropshipping] +file_filter = addons/stock_dropshipping/i18n/.po +source_file = addons/stock_dropshipping/i18n/stock_dropshipping.pot +source_lang = en + +[odoo-9.stock_invoice_directly] +file_filter = addons/stock_invoice_directly/i18n/.po +source_file = addons/stock_invoice_directly/i18n/stock_invoice_directly.pot +source_lang = en + +[odoo-9.stock_landed_costs] +file_filter = addons/stock_landed_costs/i18n/.po +source_file = addons/stock_landed_costs/i18n/stock_landed_costs.pot +source_lang = en + +[odoo-9.stock_picking_wave] +file_filter = addons/stock_picking_wave/i18n/.po +source_file = addons/stock_picking_wave/i18n/stock_picking_wave.pot +source_lang = en + +[odoo-9.subscription] +file_filter = addons/subscription/i18n/.po +source_file = addons/subscription/i18n/subscription.pot +source_lang = en + +[odoo-9.survey] +file_filter = addons/survey/i18n/.po +source_file = addons/survey/i18n/survey.pot +source_lang = en + +[odoo-9.survey_crm] +file_filter = addons/survey_crm/i18n/.po +source_file = addons/survey_crm/i18n/survey_crm.pot +source_lang = en + +[odoo-9.warning] +file_filter = addons/warning/i18n/.po +source_file = addons/warning/i18n/warning.pot +source_lang = en + +[odoo-9.web] +file_filter = addons/web/i18n/.po +source_file = addons/web/i18n/web.pot +source_lang = en + +[odoo-9.web_calendar] +file_filter = addons/web_calendar/i18n/.po +source_file = addons/web_calendar/i18n/web_calendar.pot +source_lang = en + +[odoo-9.web_diagram] +file_filter = addons/web_diagram/i18n/.po +source_file = addons/web_diagram/i18n/web_diagram.pot +source_lang = en + +[odoo-9.web_gantt] +file_filter = addons/web_gantt/i18n/.po +source_file = addons/web_gantt/i18n/web_gantt.pot +source_lang = en + +[odoo-9.web_graph] +file_filter = addons/web_graph/i18n/.po +source_file = addons/web_graph/i18n/web_graph.pot +source_lang = en + +[odoo-9.web_kanban] +file_filter = addons/web_kanban/i18n/.po +source_file = addons/web_kanban/i18n/web_kanban.pot +source_lang = en + +[odoo-9.web_kanban_gauge] +file_filter = addons/web_kanban_gauge/i18n/.po +source_file = addons/web_kanban_gauge/i18n/web_kanban_gauge.pot +source_lang = en + +[odoo-9.web_linkedin] +file_filter = addons/web_linkedin/i18n/.po +source_file = addons/web_linkedin/i18n/web_linkedin.pot +source_lang = en + +[odoo-9.web_view_editor] +file_filter = addons/web_view_editor/i18n/.po +source_file = addons/web_view_editor/i18n/web_view_editor.pot +source_lang = en + +[odoo-9.website] +file_filter = addons/website/i18n/.po +source_file = addons/website/i18n/website.pot +source_lang = en + +[odoo-9.website_blog] +file_filter = addons/website_blog/i18n/.po +source_file = addons/website_blog/i18n/website_blog.pot +source_lang = en + +[odoo-9.website_certification] +file_filter = addons/website_certification/i18n/.po +source_file = addons/website_certification/i18n/website_certification.pot +source_lang = en + +[odoo-9.website_crm] +file_filter = addons/website_crm/i18n/.po +source_file = addons/website_crm/i18n/website_crm.pot +source_lang = en + +[odoo-9.website_crm_partner_assign] +file_filter = addons/website_crm_partner_assign/i18n/.po +source_file = addons/website_crm_partner_assign/i18n/website_crm_partner_assign.pot +source_lang = en + +[odoo-9.website_customer] +file_filter = addons/website_customer/i18n/.po +source_file = addons/website_customer/i18n/website_customer.pot +source_lang = en + +[odoo-9.website_event] +file_filter = addons/website_event/i18n/.po +source_file = addons/website_event/i18n/website_event.pot +source_lang = en + +[odoo-9.website_event_sale] +file_filter = addons/website_event_sale/i18n/.po +source_file = addons/website_event_sale/i18n/website_event_sale.pot +source_lang = en + +[odoo-9.website_event_track] +file_filter = addons/website_event_track/i18n/.po +source_file = addons/website_event_track/i18n/website_event_track.pot +source_lang = en + +[odoo-9.website_forum] +file_filter = addons/website_forum/i18n/.po +source_file = addons/website_forum/i18n/website_forum.pot +source_lang = en + +[odoo-9.website_forum_doc] +file_filter = addons/website_forum_doc/i18n/.po +source_file = addons/website_forum_doc/i18n/website_forum_doc.pot +source_lang = en + +[odoo-9.website_gengo] +file_filter = addons/website_gengo/i18n/.po +source_file = addons/website_gengo/i18n/website_gengo.pot +source_lang = en + +[odoo-9.website_google_map] +file_filter = addons/website_google_map/i18n/.po +source_file = addons/website_google_map/i18n/website_google_map.pot +source_lang = en + +[odoo-9.website_hr] +file_filter = addons/website_hr/i18n/.po +source_file = addons/website_hr/i18n/website_hr.pot +source_lang = en + +[odoo-9.website_hr_recruitment] +file_filter = addons/website_hr_recruitment/i18n/.po +source_file = addons/website_hr_recruitment/i18n/website_hr_recruitment.pot +source_lang = en + +[odoo-9.website_livechat] +file_filter = addons/website_livechat/i18n/.po +source_file = addons/website_livechat/i18n/website_livechat.pot +source_lang = en + +[odoo-9.website_mail] +file_filter = addons/website_mail/i18n/.po +source_file = addons/website_mail/i18n/website_mail.pot +source_lang = en + +[odoo-9.website_mail_group] +file_filter = addons/website_mail_group/i18n/.po +source_file = addons/website_mail_group/i18n/website_mail_group.pot +source_lang = en + +[odoo-9.website_membership] +file_filter = addons/website_membership/i18n/.po +source_file = addons/website_membership/i18n/website_membership.pot +source_lang = en + +[odoo-9.website_partner] +file_filter = addons/website_partner/i18n/.po +source_file = addons/website_partner/i18n/website_partner.pot +source_lang = en + +[odoo-9.website_payment] +file_filter = addons/website_payment/i18n/.po +source_file = addons/website_payment/i18n/website_payment.pot +source_lang = en + +[odoo-9.website_project] +file_filter = addons/website_project/i18n/.po +source_file = addons/website_project/i18n/website_project.pot +source_lang = en + +[odoo-9.website_quote] +file_filter = addons/website_quote/i18n/.po +source_file = addons/website_quote/i18n/website_quote.pot +source_lang = en + +[odoo-9.website_report] +file_filter = addons/website_report/i18n/.po +source_file = addons/website_report/i18n/website_report.pot +source_lang = en + +[odoo-9.website_sale] +file_filter = addons/website_sale/i18n/.po +source_file = addons/website_sale/i18n/website_sale.pot +source_lang = en + +[odoo-9.website_sale_delivery] +file_filter = addons/website_sale_delivery/i18n/.po +source_file = addons/website_sale_delivery/i18n/website_sale_delivery.pot +source_lang = en + +[odoo-9.website_sale_options] +file_filter = addons/website_sale_options/i18n/.po +source_file = addons/website_sale_options/i18n/website_sale_options.pot +source_lang = en + +[odoo-9.website_twitter] +file_filter = addons/website_twitter/i18n/.po +source_file = addons/website_twitter/i18n/website_twitter.pot +source_lang = en + +[odoo-9.web_tests] +file_filter = addons/web_tests/i18n/.po +source_file = addons/web_tests/i18n/web_tests.pot +source_lang = en + +[odoo-9.web_tests_demo] +file_filter = addons/web_tests_demo/i18n/.po +source_file = addons/web_tests_demo/i18n/web_tests_demo.pot +source_lang = en diff --git a/addons/account/models/account_move.py b/addons/account/models/account_move.py index 4a7554c2f54..e74957f1b06 100644 --- a/addons/account/models/account_move.py +++ b/addons/account/models/account_move.py @@ -625,8 +625,7 @@ class AccountMoveLine(models.Model): amount_currency_str = formatLang(self.env, abs(actual_debit or actual_credit), currency_obj=line_currency) total_amount_currency_str = formatLang(self.env, total_amount, currency_obj=line_currency) ctx = context.copy() - if target_date: - ctx.update({'date': target_date}) + ctx.update({'date': line.date}) total_amount = line_currency.with_context(ctx).compute(total_amount, target_currency) actual_debit = line_currency.with_context(ctx).compute(actual_debit, target_currency) actual_credit = line_currency.with_context(ctx).compute(actual_credit, target_currency) diff --git a/addons/account/models/partner.py b/addons/account/models/partner.py index 654946b6fbf..0fe71725319 100644 --- a/addons/account/models/partner.py +++ b/addons/account/models/partner.py @@ -105,16 +105,16 @@ class AccountFiscalPosition(models.Model): PartnerObj = self.env['res.partner'] partner = PartnerObj.browse(partner_id) - # partner manually set fiscal position always win - if partner.property_account_position_id: - return partner.property_account_position_id.id - # if no delivery use invocing if delivery_id: delivery = PartnerObj.browse(delivery_id) else: delivery = partner + # partner manually set fiscal position always win + if delivery.property_account_position_id or partner.property_account_position_id: + return delivery.property_account_position_id.id or partner.property_account_position_id.id + domains = [[('auto_apply', '=', True), ('vat_required', '=', bool(partner.vat))]] if partner.vat: # Possibly allow fallback to non-VAT positions, if no VAT-required position matches @@ -240,9 +240,44 @@ class ResPartner(models.Model): if not self.ids: self.total_invoiced = 0.0 return True + + user_currency_id = self.env.user.company_id.currency_id.id for partner in self: - invoices = account_invoice_report.search([('partner_id', 'child_of', partner.id), ('state', 'not in', ['draft', 'cancel'])]) - partner.total_invoiced = sum(inv.user_currency_price_total for inv in invoices) + all_partner_ids = self.search([('id', 'child_of', partner.id)]).ids + + # searching account.invoice.report via the orm is comparatively expensive + # (generates queries "id in []" forcing to build the full table). + # In simple cases where all invoices are in the same currency than the user's company + # access directly these elements + + # generate where clause to include multicompany rules + where_query = account_invoice_report._where_calc([ + ('partner_id', 'in', all_partner_ids), ('state', 'not in', ['draft', 'cancel']) + ]) + account_invoice_report._apply_ir_rules(where_query, 'read') + from_clause, where_clause, where_clause_params = where_query.get_sql() + + query = """ WITH currency_rate (currency_id, rate, date_start, date_end) AS ( + SELECT r.currency_id, r.rate, r.name AS date_start, + (SELECT name FROM res_currency_rate r2 + WHERE r2.name > r.name AND + r2.currency_id = r.currency_id + ORDER BY r2.name ASC + LIMIT 1) AS date_end + FROM res_currency_rate r + ) + SELECT SUM(price_total * cr.rate) as total + FROM account_invoice_report account_invoice_report, currency_rate cr + WHERE %s + AND cr.currency_id = %%s + AND (COALESCE(account_invoice_report.date, NOW()) >= cr.date_start) + AND (COALESCE(account_invoice_report.date, NOW()) < cr.date_end OR cr.date_end IS NULL) + """ % where_clause + + # price_total is in the currency with rate = 1 + # total_invoice should be displayed in the current user's currency + self.env.cr.execute(query, where_clause_params + [user_currency_id]) + partner.total_invoiced = self.env.cr.fetchone()[0] @api.multi def _journal_item_count(self): diff --git a/addons/account/tests/test_reconciliation.py b/addons/account/tests/test_reconciliation.py index cb69a12e12e..e2e2b3a4749 100644 --- a/addons/account/tests/test_reconciliation.py +++ b/addons/account/tests/test_reconciliation.py @@ -1,5 +1,6 @@ from openerp.addons.account.tests.account_test_classes import AccountingTestCase import time +import unittest2 class TestReconciliation(AccountingTestCase): @@ -15,6 +16,8 @@ class TestReconciliation(AccountingTestCase): self.account_invoice_line_model = self.env['account.invoice.line'] self.acc_bank_stmt_model = self.env['account.bank.statement'] self.acc_bank_stmt_line_model = self.env['account.bank.statement.line'] + self.res_currency_model = self.registry('res.currency') + self.res_currency_rate_model = self.registry('res.currency.rate') self.partner_agrolait_id = self.env.ref("base.res_partner_2").id self.currency_swiss_id = self.env.ref("base.CHF").id @@ -209,3 +212,98 @@ class TestReconciliation(AccountingTestCase): self.account_euro.id: {'debit': 0.0, 'credit': 42.0, 'amount_currency': -50, 'currency_id': self.currency_swiss_id}, self.account_rcv.id: {'debit': 42.0, 'credit': 0.0, 'amount_currency': 50, 'currency_id': self.currency_swiss_id}, }) + + @unittest2.skip('adapt to new accounting') + def test_balanced_exchanges_gain_loss(self): + # The point of this test is to show that we handle correctly the gain/loss exchanges during reconciliations in foreign currencies. + # For instance, with a company set in EUR, and a USD rate set to 0.033, + # the reconciliation of an invoice of 2.00 USD (60.61 EUR) and a bank statement of two lines of 1.00 USD (30.30 EUR) + # will lead to an exchange loss, that should be handled correctly within the journal items. + cr, uid = self.cr, self.uid + # We update the currency rate of the currency USD in order to force the gain/loss exchanges in next steps + self.res_currency_rate_model.create(cr, uid, { + 'name': time.strftime('%Y-%m-%d') + ' 00:00:00', + 'currency_id': self.currency_usd_id, + 'rate': 0.033, + }) + # We create a customer invoice of 2.00 USD + invoice_id = self.account_invoice_model.create(cr, uid, { + 'partner_id': self.partner_agrolait_id, + 'currency_id': self.currency_usd_id, + 'name': 'Foreign invoice with exchange gain', + 'account_id': self.account_rcv_id, + 'type': 'out_invoice', + 'date_invoice': time.strftime('%Y-%m-%d'), + 'journal_id': self.bank_journal_usd_id, + 'invoice_line': [ + (0, 0, { + 'name': 'line that will lead to an exchange gain', + 'quantity': 1, + 'price_unit': 2, + }) + ] + }) + self.registry('account.invoice').signal_workflow(cr, uid, [invoice_id], 'invoice_open') + invoice = self.account_invoice_model.browse(cr, uid, invoice_id) + # We create a bank statement with two lines of 1.00 USD each. + bank_stmt_id = self.acc_bank_stmt_model.create(cr, uid, { + 'journal_id': self.bank_journal_usd_id, + 'date': time.strftime('%Y-%m-%d'), + 'line_ids': [ + (0, 0, { + 'name': 'half payment', + 'partner_id': self.partner_agrolait_id, + 'amount': 1.0, + 'date': time.strftime('%Y-%m-%d') + }), + (0, 0, { + 'name': 'second half payment', + 'partner_id': self.partner_agrolait_id, + 'amount': 1.0, + 'date': time.strftime('%Y-%m-%d') + }) + ] + }) + + statement = self.acc_bank_stmt_model.browse(cr, uid, bank_stmt_id) + + # We process the reconciliation of the invoice line with the two bank statement lines + line_id = None + for l in invoice.move_id.line_id: + if l.account_id.id == self.account_rcv_id: + line_id = l + break + for statement_line in statement.line_ids: + self.acc_bank_stmt_line_model.process_reconciliation(cr, uid, statement_line.id, [ + {'counterpart_move_line_id': line_id.id, 'credit': 1.0, 'debit': 0.0, 'name': line_id.name} + ]) + + # The invoice should be paid, as the payments totally cover its total + self.assertEquals(invoice.state, 'paid', 'The invoice should be paid by now') + reconcile = None + for payment in invoice.payment_ids: + reconcile = payment.reconcile_id + break + # The invoice should be reconciled (entirely, not a partial reconciliation) + self.assertTrue(reconcile, 'The invoice should be totally reconciled') + result = {} + exchange_loss_line = None + for line in reconcile.line_id: + res_account = result.setdefault(line.account_id, {'debit': 0.0, 'credit': 0.0, 'count': 0}) + res_account['debit'] = res_account['debit'] + line.debit + res_account['credit'] = res_account['credit'] + line.credit + res_account['count'] += 1 + if line.credit == 0.01: + exchange_loss_line = line + # We should be able to find a move line of 0.01 EUR on the Debtors account, being the cent we lost during the currency exchange + self.assertTrue(exchange_loss_line, 'There should be one move line of 0.01 EUR in credit') + # The journal items of the reconciliation should have their debit and credit total equal + # Besides, the total debit and total credit should be 60.61 EUR (2.00 USD) + self.assertEquals(sum([res['debit'] for res in result.values()]), 60.61) + self.assertEquals(sum([res['credit'] for res in result.values()]), 60.61) + counterpart_exchange_loss_line = None + for line in exchange_loss_line.move_id.line_id: + if line.account_id.id == self.account_fx_expense_id: + counterpart_exchange_loss_line = line + # We should be able to find a move line of 0.01 EUR on the Foreign Exchange Loss account + self.assertTrue(counterpart_exchange_loss_line, 'There should be one move line of 0.01 EUR on account "Foreign Exchange Loss"') diff --git a/addons/account/views/report_invoice.xml b/addons/account/views/report_invoice.xml index 04911aa1795..68dd852ffbc 100644 --- a/addons/account/views/report_invoice.xml +++ b/addons/account/views/report_invoice.xml @@ -8,7 +8,7 @@
- VAT: + TIN:
diff --git a/addons/account/views/res_config_view.xml b/addons/account/views/res_config_view.xml index fec1e264299..2d072115052 100644 --- a/addons/account/views/res_config_view.xml +++ b/addons/account/views/res_config_view.xml @@ -149,7 +149,7 @@
@@ -168,7 +168,7 @@
diff --git a/addons/account_analytic_analysis/__init__.py b/addons/account_analytic_analysis/__init__.py index f44526e4cfa..b27cee9bfb4 100644 --- a/addons/account_analytic_analysis/__init__.py +++ b/addons/account_analytic_analysis/__init__.py @@ -3,5 +3,3 @@ import account_analytic_analysis import res_config -import product_template -import sale_order diff --git a/addons/account_analytic_analysis/__openerp__.py b/addons/account_analytic_analysis/__openerp__.py index 4bb3e168f03..07102ba3de4 100644 --- a/addons/account_analytic_analysis/__openerp__.py +++ b/addons/account_analytic_analysis/__openerp__.py @@ -22,7 +22,6 @@ Adds menu to show relevant information to each manager.You can also view the rep 'account_analytic_analysis_cron.xml', 'res_config_view.xml', 'views/account_analytic_analysis.xml', - 'product_template_view.xml', ], 'demo': ['analytic_account_demo.xml', 'analytic_account_demo.yml'], 'test': ['../account/test/account_minimal_test.xml', 'test/account_analytic_analysis.yml'], diff --git a/addons/account_analytic_analysis/account_analytic_analysis.py b/addons/account_analytic_analysis/account_analytic_analysis.py index 5c6c682f79a..821d250d76c 100644 --- a/addons/account_analytic_analysis/account_analytic_analysis.py +++ b/addons/account_analytic_analysis/account_analytic_analysis.py @@ -28,7 +28,7 @@ class account_analytic_invoice_line(osv.osv): _columns = { 'product_id': fields.many2one('product.product','Product',required=True), - 'analytic_account_id': fields.many2one('account.analytic.account', 'Analytic Account'), + 'analytic_account_id': fields.many2one('account.analytic.account', 'Analytic Account', ondelete='cascade'), 'name': fields.text('Description', required=True), 'quantity': fields.float('Quantity', required=True), 'uom_id': fields.many2one('product.uom', 'Unit of Measure',required=True), diff --git a/addons/account_analytic_analysis/product_template.py b/addons/account_analytic_analysis/product_template.py deleted file mode 100644 index 07b3ce99f37..00000000000 --- a/addons/account_analytic_analysis/product_template.py +++ /dev/null @@ -1,17 +0,0 @@ -# -*- coding: utf-8 -*- - -from openerp import models, fields - - -class product_template(models.Model): - """ Add recurrent_invoice field to product template if it is true, - it will add to related contract. - """ - _inherit = "product.template" - - recurring_invoice = fields.Boolean( - string='Recurrent Invoice Product', default=False, - help="If selected, this product will be added to the " - "related contract (which must be associated with the SO). \n" - "It will be used as product for invoice lines and generate " - "the recurring invoices automatically") diff --git a/addons/account_analytic_analysis/product_template_view.xml b/addons/account_analytic_analysis/product_template_view.xml deleted file mode 100644 index 3a24fb3d458..00000000000 --- a/addons/account_analytic_analysis/product_template_view.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - product_template_form_view.inherit - product.template - - - - - - - - - - - - diff --git a/addons/account_analytic_analysis/sale_order.py b/addons/account_analytic_analysis/sale_order.py deleted file mode 100644 index e52e185b5df..00000000000 --- a/addons/account_analytic_analysis/sale_order.py +++ /dev/null @@ -1,25 +0,0 @@ -# -*- coding: utf-8 -*- - -from openerp import models, api - - -class sale_order_line(models.Model): - _inherit = "sale.order.line" - - @api.one - def button_confirm(self): - if self.product_id.recurring_invoice and self.order_id.project_id: - invoice_line_ids = [((0, 0, { - 'product_id': self.product_id.id, - 'analytic_account_id': self.order_id.project_id.id, - 'name': self.name, - 'quantity': self.product_uom_qty, - 'uom_id': self.product_uom.id, - 'price_unit': self.price_unit, - 'price_subtotal': self.price_subtotal - }))] - analytic_values = {'recurring_invoices': True, 'recurring_invoice_line_ids': invoice_line_ids} - if not self.order_id.project_id.partner_id: - analytic_values['partner_id'] = self.order_id.partner_id.id - self.order_id.project_id.write(analytic_values) - return super(sale_order_line, self).button_confirm() diff --git a/addons/account_budget/account_budget_view.xml b/addons/account_budget/account_budget_view.xml index 8c2be0ee5f4..3ac0561e758 100644 --- a/addons/account_budget/account_budget_view.xml +++ b/addons/account_budget/account_budget_view.xml @@ -268,6 +268,7 @@ account.analytic.account.form.inherit.budget account.analytic.account + diff --git a/addons/account_voucher/account_voucher.py b/addons/account_voucher/account_voucher.py index 4f457bf6f11..840661ce71c 100644 --- a/addons/account_voucher/account_voucher.py +++ b/addons/account_voucher/account_voucher.py @@ -104,6 +104,10 @@ class AccountVoucher(models.Model): account_id = self.partner_id.property_account_receivable_id.id elif self.journal_id.type == 'purchase': account_id = self.partner_id.property_account_payable_id.id + elif self.voucher_type == 'sale': + account_id = sef.journal_id.default_debit_account_id.id + elif self.voucher_type == 'purchase': + account_id = self.journal_id.default_credit_account_id.id else: account_id = self.journal_id.default_credit_account_id.id or self.journal_id.default_debit_account_id.id self.account_id = account_id @@ -146,6 +150,10 @@ class AccountVoucher(models.Model): account_id = partner.property_account_receivable_id.id elif journal.type == 'purchase': account_id = partner.property_account_payable_id.id + elif self.voucher_type == 'sale': + account_id = journal.default_debit_account_id.id + elif self.voucher_type == 'purchase': + account_id = journal.default_credit_account_id.id else: account_id = journal.default_credit_account_id.id or journal.default_debit_account_id.id self.account_id = account_id diff --git a/addons/auth_signup/controllers/main.py b/addons/auth_signup/controllers/main.py index 7c9c5a55b21..1518e69e63f 100644 --- a/addons/auth_signup/controllers/main.py +++ b/addons/auth_signup/controllers/main.py @@ -96,6 +96,7 @@ class AuthSignupHome(openerp.addons.web.controllers.main.Home): values = dict((key, qcontext.get(key)) for key in ('login', 'name', 'password')) assert any([k for k in values.values()]), "The form was not properly filled in." assert values.get('password') == qcontext.get('confirm_password'), "Passwords do not match; please retype them." + values['lang'] = request.lang self._signup_with_values(qcontext.get('token'), values) request.cr.commit() diff --git a/addons/barcodes/barcodes_view.xml b/addons/barcodes/barcodes_view.xml index 2011c923ff3..06ae009e11b 100644 --- a/addons/barcodes/barcodes_view.xml +++ b/addons/barcodes/barcodes_view.xml @@ -15,7 +15,7 @@

Barcodes Nomenclatures define how barcodes are recognized and categorized. - When a barcode is scanned it is associated to the first rule with a matching + When a barcode is scanned it is associated to the first rule with a matching pattern. The pattern syntax is that of regular expression, and a barcode is matched if the regular expression matches a prefix of the barcode.

diff --git a/addons/base_action_rule/base_action_rule.py b/addons/base_action_rule/base_action_rule.py index 41fe67d379c..11dc5ab5a74 100644 --- a/addons/base_action_rule/base_action_rule.py +++ b/addons/base_action_rule/base_action_rule.py @@ -184,7 +184,7 @@ class base_action_rule(osv.osv): action_model = self.pool.get('base.action.rule') action_dom = [('model', '=', self._name), ('kind', 'in', ['on_create', 'on_create_or_write'])] - action_ids = action_model.search(cr, uid, action_dom, context=context) + action_ids = action_model.search(cr, uid, action_dom, context=dict(context, active_test=True)) # check postconditions, and execute actions on the records that satisfy them for action in action_model.browse(cr, uid, action_ids, context=context): @@ -379,7 +379,7 @@ class base_action_rule(osv.osv): context = context or {} # retrieve all the action rules to run based on a timed condition action_dom = [('kind', '=', 'on_time')] - action_ids = self.search(cr, uid, action_dom, context=context) + action_ids = self.search(cr, uid, action_dom, context=dict(context, active_test=True)) for action in self.browse(cr, uid, action_ids, context=context): now = datetime.now() if action.last_run: diff --git a/addons/calendar/calendar.py b/addons/calendar/calendar.py index 84fd9a24f58..8005eed0ece 100644 --- a/addons/calendar/calendar.py +++ b/addons/calendar/calendar.py @@ -657,18 +657,18 @@ class ir_model(osv.Model): original_exp_report = openerp.service.report.exp_report -def exp_report(db, uid, object, ids, data=None, context=None): +def exp_report(db, uid, object, ids, datas=None, context=None): """ Export Report """ if object == 'printscreen.list': - original_exp_report(db, uid, object, ids, data, context) + original_exp_report(db, uid, object, ids, datas, context) new_ids = [] for id in ids: new_ids.append(calendar_id2real_id(id)) - if data.get('id', False): - data['id'] = calendar_id2real_id(data['id']) - return original_exp_report(db, uid, object, new_ids, data, context) + if datas.get('id', False): + datas['id'] = calendar_id2real_id(datas['id']) + return original_exp_report(db, uid, object, new_ids, datas, context) openerp.service.report.exp_report = exp_report diff --git a/addons/crm/crm_phonecall_view.xml b/addons/crm/crm_phonecall_view.xml index ac3a9493395..1383c996078 100644 --- a/addons/crm/crm_phonecall_view.xml +++ b/addons/crm/crm_phonecall_view.xml @@ -159,6 +159,8 @@ on_change="on_change_partner_id(partner_id)"/> + diff --git a/addons/crm/res_config_view.xml b/addons/crm/res_config_view.xml index 86299f37464..0a444d765f1 100644 --- a/addons/crm/res_config_view.xml +++ b/addons/crm/res_config_view.xml @@ -60,6 +60,13 @@

+
+

+ To be able to set your Leads Email Alias, you need to fill your alias domain first. +

+
diff --git a/addons/crm/wizard/crm_lead_to_opportunity.py b/addons/crm/wizard/crm_lead_to_opportunity.py index 576cb5afef3..e621910c71d 100644 --- a/addons/crm/wizard/crm_lead_to_opportunity.py +++ b/addons/crm/wizard/crm_lead_to_opportunity.py @@ -173,7 +173,7 @@ class crm_lead2opportunity_mass_convert(osv.osv_memory): _columns = { 'user_ids': fields.many2many('res.users', string='Salesmen'), - 'team_id': fields.many2one('crm.team', 'Sales Team', oldname='section_id'), + 'team_id': fields.many2one('crm.team', 'Sales Team', select=True, oldname='section_id'), 'deduplicate': fields.boolean('Apply deduplication', help='Merge with existing leads/opportunities of each partner'), 'action': fields.selection([ ('each_exist_or_create', 'Use existing partner or create'), diff --git a/addons/delivery/__init__.py b/addons/delivery/__init__.py index 0dd312c92d5..02d8c7c70af 100644 --- a/addons/delivery/__init__.py +++ b/addons/delivery/__init__.py @@ -5,3 +5,4 @@ import delivery import partner import sale import stock +import models diff --git a/addons/delivery/__openerp__.py b/addons/delivery/__openerp__.py index 77979915987..7257056bf4e 100644 --- a/addons/delivery/__openerp__.py +++ b/addons/delivery/__openerp__.py @@ -11,9 +11,9 @@ Allows you to add delivery methods in sale orders and picking. ============================================================== You can define your own carrier and delivery grids for prices. When creating -invoices from picking, OpenERP is able to add and compute the shipping line. +invoices from picking, the system is able to add and compute the shipping line. """, - 'author': 'OpenERP SA', + 'author': 'Odoo SA', 'depends': ['sale_stock'], 'data': [ 'security/ir.model.access.csv', diff --git a/addons/delivery/delivery.py b/addons/delivery/delivery.py index cc3bba4b0bf..41f2cc2099f 100644 --- a/addons/delivery/delivery.py +++ b/addons/delivery/delivery.py @@ -16,20 +16,6 @@ class delivery_carrier(osv.osv): _description = "Carrier" _order = 'sequence, id' - def name_get(self, cr, uid, ids, context=None): - if not len(ids): - return [] - if context is None: - context = {} - order_id = context.get('order_id',False) - if not order_id: - res = super(delivery_carrier, self).name_get(cr, uid, ids, context=context) - else: - order = self.pool.get('sale.order').browse(cr, uid, order_id, context=context) - currency = order.pricelist_id.currency_id.name or '' - res = [(r['id'], r['name']+' ('+(str(r['price']))+' '+currency+')') for r in self.read(cr, uid, ids, ['name', 'price'], context)] - return res - def get_price(self, cr, uid, ids, field_name, arg=None, context=None): res={} if context is None: diff --git a/addons/delivery/delivery_view.xml b/addons/delivery/delivery_view.xml index ea951c10d6f..525a5227cfe 100644 --- a/addons/delivery/delivery_view.xml +++ b/addons/delivery/delivery_view.xml @@ -35,8 +35,9 @@ + - + + + + + delivery.stock.picking_withcarrier.form.view.shipping + stock.picking + + + + + + + + diff --git a/addons/delivery/models/__init__.py b/addons/delivery/models/__init__.py new file mode 100644 index 00000000000..a023a5ca67b --- /dev/null +++ b/addons/delivery/models/__init__.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Odoo, Open Source Business Applications +# Copyright (c) 2015 Odoo S.A. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +import delivery_carrier +import sale_order +import stock_picking diff --git a/addons/delivery/models/delivery_carrier.py b/addons/delivery/models/delivery_carrier.py new file mode 100644 index 00000000000..1a59f182fcb --- /dev/null +++ b/addons/delivery/models/delivery_carrier.py @@ -0,0 +1,125 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Odoo, Open Source Business Applications +# Copyright (c) 2015 Odoo S.A. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +import logging +from openerp import models, fields, api +from openerp.exceptions import UserError + +_logger = logging.getLogger(__name__) + + +class DeliveryCarrier(models.Model): + _inherit = 'delivery.carrier' + ''' A Shipping Provider + + In order to add your own external provider, follow these steps: + + 1. Create your model MyProvider that _inherit 'delivery.carrier' + 2. Extend the selection of the field "delivery_type" with a pair + ('', 'My Provider') + 3. Add your methods: + _get_shipping_price_from_so + _send_shipping + _open_tracking_page + _cancel_shipment + (they are documented hereunder) + ''' + + # -------------------------------- # + # Internals for shipping providers # + # -------------------------------- # + + delivery_type = fields.Selection([('grid', 'Default Delivery')], string="Delivery Type", default='grid', required='True') + + price = fields.Float(compute='get_price') + available = fields.Boolean(compute='get_price') + + @api.one + def get_price(self): + SaleOrder = self.env['sale.order'] + + self.available = False + self.price = False + + order_id = self.env.context.get('order_id') + if order_id: + # FIXME: temporary hack until we refactor the delivery API in master + + if self.delivery_type != 'grid': + try: + order = SaleOrder.browse(order_id) + self.price = self.get_shipping_price_from_so(order)[0] + self.available = True + except UserError as e: + # no suitable delivery method found, probably configuration error + _logger.info("Carrier %s: %s, not found", self.name, e.name) + self.price = 0.0 + else: + res = super(DeliveryCarrier, self).get_price('price', []) + self.available = res[self.id]['available'] + self.price = res[self.id]['price'] + + # -------------------------- # + # API for external providers # + # -------------------------- # + + # TODO define and handle exceptions that could be thrown by providers + + def get_shipping_price_from_so(self, orders): + ''' For every sale order, compute the price of the shipment + + :param orders: A recordset of sale orders + :return list: A list of floats, containing the estimated price for the shipping of the sale order + ''' + self.ensure_one() + if hasattr(self, '%s_get_shipping_price_from_so' % self.delivery_type): + return getattr(self, '%s_get_shipping_price_from_so' % self.delivery_type)(orders) + + def send_shipping(self, pickings): + ''' Send the package to the service provider + + :param pickings: A recordset of pickings + :return list: A list of dictionaries (one per picking) containing of the form:: + { 'exact_price': price, + 'tracking_number': number } + ''' + self.ensure_one() + if hasattr(self, '%s_send_shipping' % self.delivery_type): + return getattr(self, '%s_send_shipping' % self.delivery_type)(pickings) + + def get_tracking_link(self, pickings): + ''' Ask the tracking link to the service provider + + :param pickings: A recordset of pickings + :return list: A list of string URLs, containing the tracking links for every picking + ''' + self.ensure_one() + if hasattr(self, '%s_get_tracking_link' % self.delivery_type): + return getattr(self, '%s_get_tracking_link' % self.delivery_type)(pickings) + + def cancel_shipment(self, pickings): + ''' Cancel a shipment + + :param pickings: A recordset of pickings + ''' + self.ensure_one() + if hasattr(self, '%s_cancel_shipment' % self.delivery_type): + return getattr(self, '%s_cancel_shipment' % self.delivery_type)(pickings) diff --git a/addons/delivery/models/sale_order.py b/addons/delivery/models/sale_order.py new file mode 100644 index 00000000000..31a5a261ccf --- /dev/null +++ b/addons/delivery/models/sale_order.py @@ -0,0 +1,76 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Odoo, Open Source Business Applications +# Copyright (c) 2015 Odoo S.A. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +from openerp import models, fields, api, _ +from openerp.exceptions import UserError + + +class SaleOrder(models.Model): + _inherit = 'sale.order' + + delivery_price = fields.Float(string='Estimated Delivery Price', compute='_compute_delivery_price') + + @api.depends('carrier_id', 'partner_id', 'order_line') + def _compute_delivery_price(self): + for order in self: + order.delivery_price = order.carrier_id.with_context(order_id=order.id).price + + @api.multi + def delivery_set(self): + + SaleOrderLine = self.env['sale.order.line'] + + # Remove delivery products from the sale order + self._delivery_unset() + + for order in self: + if order.carrier_id and order.carrier_id.delivery_type != 'grid': + # Shipping providers are used when delivery_type is other than 'grid' + + if order.state not in ('draft', 'sent'): + raise UserError(_('The order state have to be draft to add delivery lines.')) + + carrier = order.carrier_id + account_id = carrier.product_id.property_account_income.id + if not account_id: + account_id = carrier.product_id.categ_id.property_account_income_categ.id + + # Apply fiscal position + taxes = carrier.product_id.taxes_id + taxes_ids = taxes.ids + if order.partner_id and order.fiscal_position_id: + account_id = order.fiscal_position_id.map_account(account_id) + taxes_ids = order.fiscal_position_id.map_tax(taxes).ids + + SaleOrderLine.create({ + 'order_id': order.id, + 'name': order.carrier_id.name, + 'product_uom_qty': 1, + 'product_uom': order.carrier_id.product_id.uom_id.id, + 'product_id': order.carrier_id.product_id.id, + 'price_unit': order.carrier_id.get_shipping_price_from_so(order)[0], + 'tax_id': [(6, 0, taxes_ids)], + 'is_delivery': True + }) + + else: + # Classic grid-based carriers + super(SaleOrder, self).delivery_set() diff --git a/addons/delivery/models/stock_picking.py b/addons/delivery/models/stock_picking.py new file mode 100644 index 00000000000..355c978d366 --- /dev/null +++ b/addons/delivery/models/stock_picking.py @@ -0,0 +1,110 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Odoo, Open Source Business Applications +# Copyright (c) 2015 Odoo S.A. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +from openerp import models, fields, api + +class StockPicking(models.Model): + _inherit = 'stock.picking' + + carrier_price = fields.Float(string="Shipping Cost", readonly=True) + delivery_type = fields.Selection(related='carrier_id.delivery_type', readonly=True) + + @api.multi + def do_transfer(self): + res = super(StockPicking, self).do_transfer() + + if self.carrier_id and self.carrier_id.delivery_type != 'grid': + self.send_to_shipper() + return res + + # Signature due to strange old api methods + @api.model + def _prepare_shipping_invoice_line(self, picking, invoice): + picking.ensure_one() + invoice.ensure_one() + + carrier = picking.carrier_id + + # No carrier + if not carrier: + return None + # Carrier already invoiced on the sale order + if any(inv_line.product_id.id == carrier.product_id.id for inv_line in invoice.invoice_line_ids): + return None + + # Classic carrier + if carrier.delivery_type == 'grid': + return super(StockPicking, self)._prepare_shipping_invoice_line(picking, invoice) + + # Shipping provider + price = picking.carrier_price + + account_id = carrier.product_id.property_account_income.id + if not account_id: + account_id = carrier.product_id.categ_id.property_account_income_categ.id + + taxes = carrier.product_id.taxes_id + taxes_ids = taxes.ids + + # Apply original SO fiscal position + if picking.sale_id.fiscal_position_id: + fpos = picking.sale_id.fiscal_position_id + account_id = fpos.map_account(account_id) + taxes_ids = fpos.map_tax(taxes).ids + + res = { + 'name': carrier.name, + 'invoice_id': invoice.id, + 'uos_id': carrier.product_id.uos_id.id, + 'product_id': carrier.product_id.id, + 'account_id': account_id, + 'price_unit': price, + 'quantity': 1, + 'invoice_line_tax_id': [(6, 0, taxes_ids)], + } + + return res + + @api.one + def send_to_shipper(self): + res = self.carrier_id.send_shipping(self)[0] + self.carrier_price = res['exact_price'] + self.carrier_tracking_ref = res['tracking_number'] + msg = "Shipment sent to carrier %s for expedition with tracking number %s" % (self.carrier_id.name, self.carrier_tracking_ref) + self.message_post(body=msg) + + @api.multi + def open_website_url(self): + self.ensure_one() + + client_action = {'type': 'ir.actions.act_url', + 'name': "Shipment Tracking Page", + 'target': 'new', + 'url': self.carrier_id.get_tracking_link(self)[0] + } + return client_action + + @api.one + def cancel_shipment(self): + self.carrier_id.cancel_shipment(self) + msg = "Shipment %s cancelled" % self.carrier_tracking_ref + self.message_post(body=msg) + self.carrier_tracking_ref = False diff --git a/addons/delivery/stock.py b/addons/delivery/stock.py index 3bd3a837f30..600ac2023ac 100644 --- a/addons/delivery/stock.py +++ b/addons/delivery/stock.py @@ -64,6 +64,7 @@ class stock_picking(osv.osv): """ carrier_obj = self.pool.get('delivery.carrier') grid_obj = self.pool.get('delivery.grid') + fpos_obj = self.pool['account.fiscal.position'] if not picking.carrier_id or \ any(inv_line.product_id.id == picking.carrier_id.product_id.id for inv_line in invoice.invoice_line_ids): @@ -82,12 +83,15 @@ class stock_picking(osv.osv): .property_account_income_categ_id.id taxes = picking.carrier_id.product_id.taxes_id + taxes_ids = [x.id for x in taxes] partner = picking.partner_id or False - if partner: - account_id = self.pool.get('account.fiscal.position').map_account(cr, uid, partner.property_account_position_id, account_id) - taxes_ids = self.pool.get('account.fiscal.position').map_tax(cr, uid, partner.property_account_position_id, taxes) - else: - taxes_ids = [x.id for x in taxes] + fpos = None + if picking.sale_id and picking.sale_id.fiscal_position_id: + fpos = picking.sale_id.fiscal_position_id + elif picking.partner_id: + fpos = partner.property_account_position_id + account_id = fpos_obj.map_account(cr, uid, fpos, account_id, context=context) + taxes_ids = fpos_obj.map_tax(cr, uid, fpos, taxes, context=context) return { 'name': picking.carrier_id.name, diff --git a/addons/document/wizard/document_configuration_view.xml b/addons/document/wizard/document_configuration_view.xml index f837ebd09ef..dad1a88052e 100644 --- a/addons/document/wizard/document_configuration_view.xml +++ b/addons/document/wizard/document_configuration_view.xml @@ -29,9 +29,5 @@ new - - - automatic - diff --git a/addons/event/event.py b/addons/event/event.py index 2293d192366..b9d49e22b25 100644 --- a/addons/event/event.py +++ b/addons/event/event.py @@ -79,6 +79,9 @@ class event_event(models.Model): seats_used = fields.Integer( oldname='register_attended', string='Number of Participations', store=True, readonly=True, compute='_compute_seats') + seats_expected = fields.Integer( + string='Number of Expected Attendees', + readonly=True, compute='_compute_seats') @api.multi @api.depends('seats_max', 'registration_ids.state') @@ -107,6 +110,7 @@ class event_event(models.Model): for event in self: if event.seats_max > 0: event.seats_available = event.seats_max - (event.seats_reserved + event.seats_used) + event.seats_expected = event.seats_unconfirmed + event.seats_reserved + event.seats_used # Registration fields registration_ids = fields.One2many( @@ -250,7 +254,7 @@ class event_event(models.Model): return res @api.one - def mail_attendees(self, template_id, force_send=True, filter_func=lambda self: True): + def mail_attendees(self, template_id, force_send=False, filter_func=lambda self: True): for attendee in self.registration_ids.filtered(filter_func): self.env['mail.template'].browse(template_id).send_mail(attendee.id, force_send=force_send) @@ -336,7 +340,11 @@ class event_registration(models.Model): body=_('New registration confirmed: %s.') % (self.name or ''), subtype="event.mt_event_registration") self.state = 'open' - self.env['event.mail'].run() + + # auto-trigger after_sub (on subscribe) mail schedulers, if needed + onsubscribe_schedulers = self.event_id.event_mail_ids.filtered( + lambda s: s.interval_type == 'after_sub') + onsubscribe_schedulers.execute() @api.one def button_reg_close(self): diff --git a/addons/event/event_data.xml b/addons/event/event_data.xml index 2419132f409..b2babe25281 100644 --- a/addons/event/event_data.xml +++ b/addons/event/event_data.xml @@ -39,7 +39,7 @@ hours -1 - + event.mail run diff --git a/addons/event/event_mail.py b/addons/event/event_mail.py index 7495284c762..d6db023bcd7 100644 --- a/addons/event/event_mail.py +++ b/addons/event/event_mail.py @@ -84,9 +84,13 @@ class EventMailScheduler(models.Model): return True @api.model - def run(self): + def run(self, autocommit=False): schedulers = self.search([('done', '=', False), ('scheduled_date', '<=', datetime.strftime(fields.datetime.now(), tools.DEFAULT_SERVER_DATETIME_FORMAT))]) - return schedulers.execute() + for scheduler in schedulers: + scheduler.execute() + if autocommit: + self.env.cr.commit() + return True class EventMailRegistration(models.Model): diff --git a/addons/event/event_view.xml b/addons/event/event_view.xml index f291824afe7..bd90a57919c 100644 --- a/addons/event/event_view.xml +++ b/addons/event/event_view.xml @@ -48,7 +48,7 @@ form Attendees tree,form,calendar,graph - {'search_default_event_id': active_id, 'default_event_id': active_id, 'search_default_not_cancelled': 1} + {'search_default_event_id': active_id, 'default_event_id': active_id, 'search_default_expected': True} @@ -71,7 +71,7 @@ class="oe_stat_button oe_event_attendee_button" icon="fa-github" help="Register with this event"> - +
@@ -158,6 +158,8 @@ + + @@ -191,11 +193,11 @@

- Attendees + Attendees Expected - / - Expected - (Report) +
+ Attendees Confirmed + (Report)

@@ -368,7 +370,7 @@ - + diff --git a/addons/event/tests/test_mail_schedule.py b/addons/event/tests/test_mail_schedule.py index c712e48919f..f92cf0fcc5b 100644 --- a/addons/event/tests/test_mail_schedule.py +++ b/addons/event/tests/test_mail_schedule.py @@ -48,27 +48,12 @@ class TestMailSchedule(TestEventCommon): 'email': 'reg1@example.com', }) - # check before event scheduler - schedulers = self.EventMail.search([('event_id', '=', test_event.id), ('interval_type', '=', 'before_event')]) - self.assertEqual(len(schedulers), 1, 'event: wrong scheduler creation') - self.assertEqual(schedulers[0].scheduled_date, datetime.datetime.strftime(event_date_begin + relativedelta(days=-2), tools.DEFAULT_SERVER_DATETIME_FORMAT), 'event: incorrect scheduled date') - - # execute event reminder scheduler - schedulers[0].execute() - - self.assertTrue(schedulers[0].mail_sent, 'event: reminder scheduler should have sent an email') - self.assertTrue(schedulers[0].done, 'event: reminder scheduler should be done') - - mails = self.env['mail.mail'].search([('subject', 'ilike', 'reminder'), ('date', '>=', datetime.datetime.strftime(now, tools.DEFAULT_SERVER_DATETIME_FORMAT))], order='date DESC', limit=3) - self.assertEqual(len(mails), 0, 'event: reminder to sent is not empty') - # check subscription scheduler schedulers = self.EventMail.search([('event_id', '=', test_event.id), ('interval_type', '=', 'after_sub')]) self.assertEqual(len(schedulers), 1, 'event: wrong scheduler creation') self.assertEqual(schedulers[0].scheduled_date, test_event.create_date, 'event: incorrect scheduled date for checking controller') - # execute subscription scheduler - schedulers[0].execute() + # verify that subscription scheduler was auto-executed after each registration self.assertEqual(len(schedulers[0].mail_registration_ids), 2, 'event: incorrect number of mail scheduled date') mails = self.env['mail.mail'].search([('subject', 'ilike', 'subscription'), ('date', '>=', datetime.datetime.strftime(now, tools.DEFAULT_SERVER_DATETIME_FORMAT))], order='date DESC', limit=3) @@ -76,3 +61,19 @@ class TestMailSchedule(TestEventCommon): for registration in schedulers[0].mail_registration_ids: self.assertTrue(registration.mail_sent, 'event: wrongly confirmed mailing on subscription') + + # check before event scheduler + schedulers = self.EventMail.search([('event_id', '=', test_event.id), ('interval_type', '=', 'before_event')]) + self.assertEqual(len(schedulers), 1, 'event: wrong scheduler creation') + self.assertEqual(schedulers[0].scheduled_date, datetime.datetime.strftime(event_date_begin + relativedelta(days=-2), tools.DEFAULT_SERVER_DATETIME_FORMAT), 'event: incorrect scheduled date') + + # execute event reminder scheduler explicitly + schedulers[0].execute() + + self.assertTrue(schedulers[0].mail_sent, 'event: reminder scheduler should have sent an email') + self.assertTrue(schedulers[0].done, 'event: reminder scheduler should be done') + + mails = self.env['mail.mail'].search([('subject', 'ilike', 'reminder'), ('date', '>=', datetime.datetime.strftime(now, tools.DEFAULT_SERVER_DATETIME_FORMAT))], order='date DESC', limit=3) + self.assertEqual(len(mails), 2, 'event: wrong number of reminders in outgoing mail queue') + + diff --git a/addons/google_calendar/google_calendar.py b/addons/google_calendar/google_calendar.py index df90f78f5d1..1bcbad15413 100644 --- a/addons/google_calendar/google_calendar.py +++ b/addons/google_calendar/google_calendar.py @@ -85,12 +85,13 @@ class SyncEvent(object): def compute_OP(self, modeFull=True): #If event are already in Gmail and in OpenERP if self.OE.found and self.GG.found: + is_owner = self.OE.event.env.user.id == self.OE.event.user_id.id #If the event has been deleted from one side, we delete on other side ! - if self.OE.status != self.GG.status: + if self.OE.status != self.GG.status and is_owner: self.OP = Delete((self.OE.status and "OE") or (self.GG.status and "GG"), 'The event has been deleted from one side, we delete on other side !') #If event is not deleted ! - elif self.OE.status and self.GG.status: + elif self.OE.status and (self.GG.status or not is_owner): if self.OE.update.split('.')[0] != self.GG.update.split('.')[0]: if self.OE.update < self.GG.update: tmpSrc = 'GG' @@ -423,7 +424,7 @@ class google_calendar(osv.AbstractModel): if type == "write": for oe_attendee in event['attendee_ids']: if oe_attendee.email == google_attendee['email']: - calendar_attendee_obj.write(cr, uid, [oe_attendee.id], {'state': google_attendee['responseStatus']}, context=context) + calendar_attendee_obj.write(cr, uid, [oe_attendee.id], {'state': google_attendee['responseStatus'], 'google_internal_event_id': single_event_dict.get('id')}, context=context) google_attendee['found'] = True continue @@ -442,6 +443,7 @@ class google_calendar(osv.AbstractModel): partner_record.append((4, attendee.get('id'))) attendee['partner_id'] = attendee.pop('id') attendee['state'] = google_attendee['responseStatus'] + attendee['google_internal_event_id'] = single_event_dict.get('id') attendee_record.append((0, 0, attendee)) for google_alarm in single_event_dict.get('reminders', {}).get('overrides', []): alarm_id = calendar_alarm_obj.search( @@ -626,7 +628,7 @@ class google_calendar(osv.AbstractModel): update_date = datetime.strptime(response['updated'], "%Y-%m-%dT%H:%M:%S.%fz") ev_obj.write(cr, uid, att.event_id.id, {'oe_update_date': update_date}) new_ids.append(response['id']) - att_obj.write(cr, uid, [att.id], {'google_internal_event_id': response['id'], 'oe_synchro_date': update_date}) + att_obj.write(cr, uid, [att.id for att in att.event_id.attendee_ids], {'google_internal_event_id': response['id'], 'oe_synchro_date': update_date}) cr.commit() else: _logger.warning("Impossible to create event %s. [%s] Enable DEBUG for response detail.", att.event_id.id, st) @@ -946,7 +948,7 @@ class google_calendar(osv.AbstractModel): self.pool['res.users'].write(cr, SUPERUSER_ID, uid, vals, context=context) def get_minTime(self, cr, uid, context=None): - number_of_week = self.pool['ir.config_parameter'].get_param(cr, uid, 'calendar.week_synchro', default=13) + number_of_week = int(self.pool['ir.config_parameter'].get_param(cr, uid, 'calendar.week_synchro', default=13)) return datetime.now() - timedelta(weeks=number_of_week) def get_need_synchro_attendee(self, cr, uid, context=None): diff --git a/addons/hr_applicant_document/views/hr_applicant.xml b/addons/hr_applicant_document/views/hr_applicant.xml index d5582db34f5..49ebec5946b 100644 --- a/addons/hr_applicant_document/views/hr_applicant.xml +++ b/addons/hr_applicant_document/views/hr_applicant.xml @@ -20,6 +20,7 @@ kanban,tree,form [('res_model','=','hr.applicant')] +

Search through resumes and motivation letters. diff --git a/addons/hr_gamification/wizard/grant_badge.py b/addons/hr_gamification/wizard/grant_badge.py index e03fbfe9ad8..634a6f8fd28 100644 --- a/addons/hr_gamification/wizard/grant_badge.py +++ b/addons/hr_gamification/wizard/grant_badge.py @@ -13,7 +13,7 @@ class hr_grant_badge_wizard(osv.TransientModel): 'employee_id': fields.many2one("hr.employee", string='Employee', required=True), 'user_id': fields.related("employee_id", "user_id", type="many2one", relation="res.users", - store=True, string='User') + store=True, string='User', required=True) } def action_grant_badge(self, cr, uid, ids, context=None): @@ -40,4 +40,4 @@ class hr_grant_badge_wizard(osv.TransientModel): badge_user = badge_user_obj.create(cr, uid, values, context=context) result = badge_user_obj._send_badge(cr, uid, [badge_user], context=context) - return result \ No newline at end of file + return result diff --git a/addons/hr_payroll/hr_payroll.py b/addons/hr_payroll/hr_payroll.py index d449015405e..2f221802dd2 100644 --- a/addons/hr_payroll/hr_payroll.py +++ b/addons/hr_payroll/hr_payroll.py @@ -262,7 +262,7 @@ class hr_payslip(osv.osv): \n* When user cancel payslip the status is \'Rejected\'.'), 'line_ids': one2many_mod2('hr.payslip.line', 'slip_id', 'Payslip Lines', readonly=True, states={'draft':[('readonly',False)]}), 'company_id': fields.many2one('res.company', 'Company', required=False, readonly=True, states={'draft': [('readonly', False)]}, copy=False), - 'worked_days_line_ids': fields.one2many('hr.payslip.worked_days', 'payslip_id', 'Payslip Worked Days', required=False, readonly=True, states={'draft': [('readonly', False)]}), + 'worked_days_line_ids': fields.one2many('hr.payslip.worked_days', 'payslip_id', 'Payslip Worked Days', copy=True, required=False, readonly=True, states={'draft': [('readonly', False)]}), 'input_line_ids': fields.one2many('hr.payslip.input', 'payslip_id', 'Payslip Inputs', required=False, readonly=True, states={'draft': [('readonly', False)]}), 'paid': fields.boolean('Made Payment Order ? ', required=False, readonly=True, states={'draft': [('readonly', False)]}, copy=False), 'note': fields.text('Internal Note', readonly=True, states={'draft':[('readonly',False)]}), @@ -304,7 +304,6 @@ class hr_payslip(osv.osv): mod_obj = self.pool.get('ir.model.data') for payslip in self.browse(cr, uid, ids, context=context): id_copy = self.copy(cr, uid, payslip.id, {'credit_note': True, 'name': _('Refund: ')+payslip.name}, context=context) - self.compute_sheet(cr, uid, [id_copy], context=context) self.signal_workflow(cr, uid, [id_copy], 'hr_verify_sheet') self.signal_workflow(cr, uid, [id_copy], 'process_sheet') diff --git a/addons/hr_timesheet_invoice/hr_timesheet_invoice_view.xml b/addons/hr_timesheet_invoice/hr_timesheet_invoice_view.xml index 967da3c3f17..69837e9fe72 100644 --- a/addons/hr_timesheet_invoice/hr_timesheet_invoice_view.xml +++ b/addons/hr_timesheet_invoice/hr_timesheet_invoice_view.xml @@ -14,7 +14,7 @@ -

+