From cbe2dbb672a6c55d86f2e515f9d1cdfee426ef80 Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Sun, 6 Jul 2014 16:44:26 +0200 Subject: [PATCH] [MERGE] new v8 api by rco A squashed merge is required as the conversion of the apiculture branch from bzr to git was not correctly done. The git history contains irrelevant blobs and commits. This branch brings a lot of changes and fixes, too many to list exhaustively. - New orm api, objects are now used instead of ids - Environements to encapsulates cr uid context while maintaining backward compatibility - Field compute attribute is a new object oriented way to define function fields - Shared browse record cache - New onchange protocol - Optional copy flag on fields - Documentation update - Dead code cleanup - Lots of fixes --- addons/account/account.py | 158 +- addons/account/account_bank_statement.py | 53 +- addons/account/account_cash_statement.py | 4 +- addons/account/account_invoice.py | 2436 ++++--- addons/account/account_invoice_view.xml | 8 +- addons/account/account_move_line.py | 41 +- addons/account/account_view.xml | 2 +- addons/account/edi/invoice.py | 2 +- addons/account/ir_sequence.py | 5 +- addons/account/partner.py | 36 +- addons/account/report/account_journal.py | 1 + addons/account/report/account_report.py | 36 +- .../wizard/account_automatic_reconcile.py | 2 +- addons/account/wizard/account_chart.py | 2 +- .../wizard/account_fiscalyear_close.py | 6 +- .../wizard/account_fiscalyear_close_state.py | 5 + .../account/wizard/account_invoice_refund.py | 8 +- .../account/wizard/account_invoice_state.py | 22 +- .../wizard/account_open_closed_fiscalyear.py | 1 + addons/account/wizard/account_period_close.py | 2 + addons/account/wizard/account_reconcile.py | 1 + addons/account/wizard/account_state_open.py | 12 +- .../wizard/account_statement_from_invoice.py | 3 +- addons/account/wizard/account_use_model.py | 2 +- .../account_analytic_analysis.py | 8 +- .../account_analytic_plans.py | 26 +- .../wizard/account_crossovered_analytic.py | 2 +- .../account_anglo_saxon/test/anglo_saxon.yml | 14 +- .../test/anglo_saxon_avg_fifo.yml | 12 +- addons/account_asset/account_asset.py | 25 +- .../account_bank_statement.py | 14 +- addons/account_budget/account_budget.py | 4 +- .../wizard/account_check_batch_printing.py | 4 +- addons/account_followup/account_followup.py | 23 +- .../report/account_followup_print.py | 4 +- .../tests/test_account_followup.py | 5 +- .../wizard/account_followup_print.py | 5 +- addons/account_payment/account_payment.py | 24 +- .../test/payment_order_process.yml | 2 +- .../wizard/account_payment_order.py | 2 +- .../account_payment_populate_statement.py | 4 +- addons/account_sequence/account_sequence.py | 4 +- addons/account_voucher/account_voucher.py | 44 +- addons/account_voucher/test/case1_usd_usd.yml | 4 +- .../test/case1_usd_usd_payment_rate.yml | 2 +- .../test/case2_suppl_usd_eur.yml | 4 +- .../test/case2_usd_eur_debtor_in_eur.yml | 4 +- .../test/case2_usd_eur_debtor_in_usd.yml | 4 +- addons/account_voucher/test/case3_eur_eur.yml | 5 +- addons/account_voucher/test/case4_cad_chf.yml | 17 +- .../test/case5_suppl_usd_usd.yml | 11 +- addons/account_voucher/test/case_eur_usd.yml | 6 +- addons/account_voucher/test/sales_payment.yml | 2 +- addons/account_voucher/test/sales_receipt.yml | 2 +- addons/analytic/analytic.py | 19 +- .../analytic_contract_hr_expense.py | 3 +- .../analytic_user_function.py | 2 +- addons/auth_crypt/auth_crypt.py | 4 +- addons/auth_crypt/i18n/base_crypt.pot | 22 - addons/auth_crypt/i18n/bg.po | 76 - addons/auth_crypt/i18n/ca.po | 78 - addons/auth_crypt/i18n/el.po | 24 - addons/auth_crypt/i18n/es_CL.po | 80 - addons/auth_crypt/i18n/es_CR.po | 79 - addons/auth_crypt/i18n/es_MX.po | 86 - addons/auth_crypt/i18n/es_PY.po | 78 - addons/auth_crypt/i18n/es_VE.po | 86 - addons/auth_crypt/i18n/fa.po | 23 - addons/auth_crypt/i18n/fi.po | 40 - addons/auth_crypt/i18n/gu.po | 37 - addons/auth_crypt/i18n/id.po | 23 - addons/auth_crypt/i18n/ja.po | 40 - addons/auth_crypt/i18n/lv.po | 40 - addons/auth_crypt/i18n/nb.po | 44 - addons/auth_crypt/i18n/oc.po | 45 - addons/auth_crypt/i18n/sk.po | 41 - addons/auth_crypt/i18n/sq.po | 23 - addons/auth_crypt/i18n/sr@latin.po | 76 - addons/auth_crypt/i18n/vi.po | 80 - addons/auth_ldap/users_ldap.py | 6 +- addons/auth_oauth/res_users.py | 4 +- addons/auth_openid/res_users.py | 22 +- addons/auth_signup/res_users.py | 8 +- addons/auth_signup/signup.xml | 42 - addons/base_action_rule/base_action_rule.py | 123 +- addons/base_gengo/res_company.py | 4 +- addons/base_import/models.py | 6 +- addons/base_import/tests/test_cases.py | 54 +- .../base_report_designer.py | 4 +- .../wizard/base_report_designer_modify.py | 11 +- addons/board/controllers.py | 2 +- addons/calendar/base_calendar_view.xml | 291 - addons/calendar/calendar.py | 24 +- addons/calendar/controllers/main.py | 6 +- addons/calendar/crm_meeting.py | 176 - addons/calendar/crm_meeting_data.xml | 32 - addons/crm/base_partner_merge.py | 5 +- addons/crm/crm_lead.py | 12 +- addons/crm/crm_segmentation.py | 7 +- addons/crm/res_partner.py | 8 - addons/crm/sales_team.py | 4 +- .../test/lead2opportunity_assign_salesmen.yml | 10 +- addons/crm/wizard/crm_lead_to_opportunity.py | 2 +- addons/crm/wizard/crm_merge_opportunities.py | 3 +- addons/crm_claim/crm_claim.py | 3 +- addons/crm_partner_assign/crm_lead.py | 2 +- addons/crm_profiling/crm_profiling.py | 5 +- .../wizard/open_questionnaire.py | 2 +- addons/delivery/delivery.py | 2 +- addons/document/document.py | 5 +- addons/edi/models/edi.py | 6 +- addons/email_template/email_template.py | 18 +- addons/email_template/ir_actions.py | 2 +- addons/email_template/tests/test_mail.py | 2 +- .../wizard/mail_compose_message.py | 2 +- addons/event/event.py | 676 +- addons/event/event_view.xml | 8 +- .../event/report/report_event_registration.py | 43 +- addons/event/res_partner.py | 9 +- .../event/test/process/event_draft2done.yml | 128 +- addons/event/test/ui/demo_data.yml | 17 +- addons/event/test/ui/duplicate_event.yml | 16 +- addons/event/wizard/event_confirm.py | 17 +- addons/event_sale/event_sale.py | 71 +- addons/fetchmail/fetchmail.py | 5 +- addons/fleet/fleet.py | 42 +- addons/gamification/models/challenge.py | 6 +- addons/gamification/models/goal.py | 3 +- addons/google_account/controllers/main.py | 6 +- addons/google_account/google_account.py | 3 +- addons/google_calendar/google_calendar.py | 4 +- addons/google_drive/google_drive.py | 2 +- addons/google_drive/static/src/xml/gdocs.xml | 8 - .../google_spreadsheet/google_spreadsheet.py | 2 +- addons/hr/hr.py | 39 +- addons/hr/images/photo.png | Bin 6250 -> 0 bytes addons/hr/res_users.py | 1 + addons/hr_evaluation/hr_evaluation.py | 19 +- addons/hr_expense/hr_expense.py | 30 +- addons/hr_holidays/hr_holidays.py | 55 +- .../report/holidays_summary_report.py | 20 +- addons/hr_holidays/test/test_hr_holiday.yml | 2 +- .../hr_holidays/tests/test_holidays_flow.py | 25 +- .../wizard/hr_holidays_summary_department.py | 2 +- .../wizard/hr_holidays_summary_employees.py | 2 +- addons/hr_payroll/hr_payroll.py | 76 +- ...hr_payroll_contribution_register_report.py | 2 +- .../hr_payroll_payslips_by_employees.py | 2 +- .../hr_payroll_account/hr_payroll_account.py | 8 +- .../test/hr_payroll_account.yml | 2 +- .../hr_payroll_payslips_by_employees.py | 7 +- addons/hr_recruitment/hr_recruitment.py | 10 +- .../hr_recruitment_create_partner_job.py | 4 +- .../hr_timesheet_invoice.py | 27 +- .../test/test_hr_timesheet_invoice.yml | 2 +- .../test_hr_timesheet_invoice_no_prod_tax.yml | 2 +- .../wizard/hr_timesheet_analytic_profit.py | 2 +- .../hr_timesheet_final_invoice_create.py | 4 +- .../wizard/hr_timesheet_invoice_create.py | 8 +- .../hr_timesheet_sheet/hr_timesheet_sheet.py | 2 +- .../test/test_hr_timesheet_sheet.yml | 4 +- addons/im_livechat/im_livechat.py | 8 +- addons/im_livechat/views/im_livechat_view.xml | 4 +- .../wizard/l10n_be_account_vat_declaration.py | 1 + addons/l10n_be/wizard/l10n_be_vat_intra.py | 1 + addons/l10n_be_invoice_bba/invoice.py | 2 + addons/l10n_fr/l10n_fr.py | 2 +- .../l10n_in_hr_payroll/l10n_in_hr_payroll.py | 28 +- addons/l10n_ma/l10n_ma.py | 2 +- addons/l10n_uk/i18n/l10n_chart_uk_minimal.pot | 108 - addons/lunch/lunch.py | 13 +- addons/lunch/tests/test_lunch.py | 8 +- addons/mail/mail_followers.py | 31 +- addons/mail/mail_group.py | 11 +- addons/mail/mail_group_menu.py | 2 +- addons/mail/mail_mail.py | 13 +- addons/mail/mail_message.py | 45 +- addons/mail/mail_thread.py | 32 +- addons/mail/res_users.py | 2 +- addons/mail/static/scripts/__init__.py | 22 - .../mail/static/scripts/openerp_mailgate.py | 8 +- .../scripts/openerp_mailgate/__init__.py | 22 - addons/mail/static/src/js/mail_followers.js | 4 +- addons/mail/tests/common.py | 30 +- addons/mail/tests/test_mail_features.py | 14 +- addons/mail/tests/test_mail_gateway.py | 12 +- addons/mail/tests/test_mail_group.py | 2 +- addons/mail/tests/test_mail_message.py | 61 +- addons/mail/tests/test_message_read.py | 2 +- addons/mail/update.py | 2 +- addons/mail/wizard/mail_compose_message.py | 3 +- .../marketing_campaign/marketing_campaign.py | 83 +- addons/mass_mailing/models/mass_mailing.py | 14 +- addons/membership/membership.py | 17 +- addons/membership/membership_demo.yml | 2 +- addons/membership/test/test_membership.yml | 2 +- addons/mrp/mrp.py | 32 +- addons/mrp/product.py | 8 - addons/mrp/stock.py | 8 +- addons/mrp/tests/__init__.py | 28 - addons/mrp_byproduct/mrp_byproduct.py | 2 +- addons/mrp_operations/mrp_operations.py | 60 +- .../test/workcenter_operations.yml | 18 +- addons/mrp_repair/mrp_repair.py | 53 +- .../mrp_repair/security/ir.model.access.csv | 1 + addons/mrp_repair/wizard/make_invoice.py | 2 +- addons/multi_company/multi_company_demo.xml | 9 + addons/payment/models/payment_acquirer.py | 4 +- .../point_of_sale/account_bank_statement.py | 2 +- addons/point_of_sale/point_of_sale.py | 73 +- .../test/01_order_to_payment.yml | 4 +- addons/point_of_sale/wizard/pos_confirm.py | 2 +- addons/point_of_sale/wizard/pos_payment.py | 3 +- .../wizard/pos_session_opening.py | 4 +- addons/portal/tests/test_portal.py | 6 +- .../tests/test_access_rights.py | 43 +- .../tests/test_access_rights.py | 2 +- addons/procurement/procurement.py | 2 +- addons/procurement/wizard/schedulers_all.py | 24 +- addons/procurement_jit/mrp_jit.xml | 14 - addons/product/pricelist.py | 32 +- addons/product/product.py | 10 +- addons/product_expiry/product_expiry.py | 3 +- .../product_margin/wizard/product_margin.py | 61 +- .../product_visible_discount.py | 2 +- addons/project/project.py | 81 +- addons/project/project_view.xml | 12 +- addons/project/tests/test_project_flow.py | 6 +- .../project/wizard/project_task_delegate.py | 2 +- addons/project_issue/project_issue.py | 12 +- addons/project_timesheet/project_timesheet.py | 1 + addons/purchase/purchase.py | 105 +- addons/purchase/purchase_data.yml | 2 +- addons/purchase/stock.py | 4 +- addons/purchase/test/fifo_price.yml | 44 +- .../purchase/test/process/rfq2order2done.yml | 8 +- .../purchase/test/process/run_scheduler.yml | 2 +- .../purchase_requisition.py | 39 +- .../test/purchase_requisition.yml | 2 +- addons/report/models/abstract_report.py | 3 +- addons/report/models/report.py | 29 +- addons/report_webkit/__openerp__.py | 2 +- addons/report_webkit/webkit_report.py | 15 +- addons/resource/resource.py | 15 +- addons/sale/res_partner.py | 9 - addons/sale/sale.py | 65 +- addons/sale/sales_team.py | 21 +- addons/sale/test/cancel_order.yml | 3 +- addons/sale/test/manual_order_policy.yml | 2 +- addons/sale/wizard/sale_line_invoice.py | 1 + addons/sale_crm/sale_crm.py | 5 - addons/sale_crm/wizard/crm_make_sale.py | 5 +- addons/sale_mrp/test/sale_mrp.yml | 2 +- addons/sale_order_dates/sale_order_dates.py | 7 +- addons/sale_service/models/sale_service.py | 4 +- addons/sale_stock/sale_stock.py | 28 - .../test/cancel_order_sale_stock.yml | 2 +- .../sale_stock/test/picking_order_policy.yml | 14 +- addons/share/wizard/share_wizard.py | 11 +- addons/stock/product.py | 2 +- addons/stock/stock.py | 124 +- addons/stock/test/shipment.yml | 7 +- addons/stock_account/stock.py | 4 +- addons/stock_account/stock_account.py | 3 +- .../wizard/stock_invoice_onshipping.py | 2 +- addons/stock_dropshipping/test/lifo_price.yml | 12 +- .../stock_invoice_directly.py | 4 +- .../test/stock_invoice_directly.yml | 4 +- .../stock_landed_costs/stock_landed_costs.py | 19 +- .../stock_picking_wave/stock_picking_wave.py | 16 +- addons/subscription/subscription.py | 4 +- addons/survey/controllers/main.py | 1 + addons/survey/survey.py | 43 +- addons/web/static/src/js/chrome.js | 5 +- addons/web/static/src/js/view_form.js | 312 +- .../web/static/src/js/view_list_editable.js | 2 +- addons/web/static/src/js/views.js | 6 +- addons/web/static/src/xml/base.xml | 2 +- addons/web/tests/test_js.py | 2 +- addons/website/models/ir_actions.py | 4 +- addons/website/models/ir_http.py | 8 +- addons/website/models/ir_ui_view.py | 2 +- addons/website_blog/controllers/main.py | 3 +- addons/website_blog/models/website_blog.py | 13 +- .../views/website_blog_templates.xml | 2 +- .../wizard/document_page_create_menu.py | 86 - .../wizard/document_page_create_menu_view.xml | 33 - addons/website_crm/controllers/main.py | 9 +- .../controllers/main.py | 2 +- .../models/res_partner.py | 2 +- addons/website_event/models/event.py | 2 +- .../website_event_sale/models/sale_order.py | 2 +- addons/website_event_track/models/event.py | 11 +- addons/website_forum_doc/controllers/main.py | 4 +- addons/website_gengo/controllers/main.py | 4 +- addons/website_hr/controllers/main.py | 4 +- addons/website_hr/models/hr.py | 2 +- .../controllers/main.py | 4 +- .../website_hr_recruitment/models/hr_job.py | 2 +- .../controllers/email_designer.py | 2 +- addons/website_mail/models/mail_message.py | 2 +- addons/website_mail/models/mail_thread.py | 5 - addons/website_membership/controllers/main.py | 2 +- addons/website_membership/models/product.py | 2 +- addons/website_partner/models/res_partner.py | 2 +- addons/website_quote/models/order.py | 6 +- addons/website_sale/controllers/main.py | 6 +- addons/website_sale/models/product.py | 2 +- .../models/sale_order.py | 2 +- doc/03_module_dev_02.rst | 1 + doc/03_module_dev_03.rst | 17 +- doc/api_models.rst | 18 +- doc/howto/howto_website.rst | 30 +- doc/howto/howto_website/basic-page | 4 +- doc/howto/howto_website/field-label | 22 +- doc/howto/howto_website/lectures-model-add | 40 +- doc/howto/howto_website/manifest | 11 +- .../howto_website/move-to-openerp-objects | 70 +- doc/howto/howto_website/ta-controller | 6 +- doc/howto/howto_website/ta-data | 2 +- doc/howto/howto_website/ta-html-biography | 17 +- doc/howto/howto_website/ta-model | 34 +- doc/howto/howto_website/ta-t-field | 11 +- doc/howto/howto_website/ta-view-fix | 15 +- doc/howto/howto_website/templates-basic | 29 +- doc/howto/howto_website/website-dependency | 28 +- doc/howto/howto_website/website-layoutify | 14 +- doc/index.rst | 7 +- doc/new_api.rst | 138 + openerp/__init__.py | 17 +- openerp/addons/base/__openerp__.py | 3 +- openerp/addons/base/base_menu.xml | 4 + openerp/addons/base/ir/ir_actions.py | 18 +- openerp/addons/base/ir/ir_attachment.py | 8 +- openerp/addons/base/ir/ir_cron.py | 44 +- openerp/addons/base/ir/ir_exports.py | 2 +- openerp/addons/base/ir/ir_fields.py | 10 +- openerp/addons/base/ir/ir_http.py | 8 +- openerp/addons/base/ir/ir_mail_server.py | 21 +- openerp/addons/base/ir/ir_model.py | 73 +- openerp/addons/base/ir/ir_qweb.py | 14 +- openerp/addons/base/ir/ir_qweb.xml | 4 +- openerp/addons/base/ir/ir_rule.py | 4 +- openerp/addons/base/ir/ir_sequence.py | 7 +- openerp/addons/base/ir/ir_translation.py | 6 +- openerp/addons/base/ir/ir_ui_menu.py | 123 +- openerp/addons/base/ir/ir_ui_view.py | 68 +- openerp/addons/base/ir/ir_values.py | 29 +- openerp/addons/base/module/module.py | 103 +- .../module/wizard/base_import_language.py | 2 +- .../base/module/wizard/base_module_update.py | 50 +- .../module/wizard/base_module_update_view.xml | 4 +- openerp/addons/base/res/ir_property.py | 186 +- openerp/addons/base/res/res_company.py | 6 +- openerp/addons/base/res/res_config.py | 6 +- openerp/addons/base/res/res_currency.py | 78 +- openerp/addons/base/res/res_partner.py | 287 +- openerp/addons/base/res/res_users.py | 48 +- .../addons/base/security/base_security.xml | 6 - openerp/addons/base/tests/__init__.py | 3 +- openerp/addons/base/tests/base_test.yml | 7 +- openerp/addons/base/tests/test_acl.py | 54 +- openerp/addons/base/tests/test_api.py | 444 ++ openerp/addons/base/tests/test_ir_actions.py | 2 +- openerp/addons/base/tests/test_ir_rule.yml | 2 +- openerp/addons/base/tests/test_orm.py | 31 +- .../addons/base/tests/test_osv_expression.yml | 4 +- openerp/addons/base/tests/test_views.py | 3 + openerp/addons/base/tests/test_xmlrpc.py | 2 +- openerp/addons/base/workflow/workflow.py | 4 + openerp/addons/test_exceptions/models.py | 5 +- openerp/addons/test_impex/models.py | 94 +- .../addons/test_impex/tests/test_export.py | 17 +- .../addons/test_impex/tests/test_import.py | 10 +- openerp/addons/test_impex/tests/test_load.py | 18 +- openerp/addons/test_inherit/__init__.py | 3 + openerp/addons/test_inherit/__openerp__.py | 15 + .../addons/test_inherit/ir.model.access.csv | 3 + openerp/addons/test_inherit/models.py | 47 + openerp/addons/test_inherit/tests/__init__.py | 12 + .../addons/test_inherit/tests/test_inherit.py | 36 + openerp/addons/test_new_api/__init__.py | 2 + openerp/addons/test_new_api/__openerp__.py | 19 + openerp/addons/test_new_api/demo_data.xml | 30 + .../addons/test_new_api/ir.model.access.csv | 6 + openerp/addons/test_new_api/models.py | 182 + openerp/addons/test_new_api/tests/__init__.py | 18 + .../test_new_api/tests/test_attributes.py | 25 + .../tests/test_field_conversions.py | 13 + .../test_new_api/tests/test_new_fields.py | 393 ++ .../test_new_api/tests/test_onchange.py | 165 + .../tests/test_related.py} | 61 +- openerp/addons/test_new_api/views.xml | 127 + openerp/addons/test_workflow/models.py | 17 +- .../test_workflow/tests/test_workflow.py | 2 +- openerp/api.py | 814 +++ openerp/cli/scaffold/models.jinja2 | 9 +- openerp/exceptions.py | 16 +- openerp/fields.py | 1563 +++++ openerp/http.py | 18 +- openerp/models.py | 5800 +++++++++++++++++ openerp/modules/loading.py | 5 + openerp/modules/module.py | 2 +- openerp/modules/registry.py | 103 +- openerp/osv/__init__.py | 2 - openerp/osv/expression.py | 236 +- openerp/osv/fields.py | 433 +- openerp/osv/orm.py | 5155 +-------------- openerp/osv/osv.py | 3 +- openerp/report/custom.py | 11 +- openerp/report/print_xml.py | 42 +- openerp/report/report_sxw.py | 123 +- openerp/service/model.py | 2 +- openerp/service/security.py | 3 +- openerp/service/wsgi_server.py | 17 +- openerp/tests/common.py | 3 + openerp/tools/__init__.py | 1 + openerp/tools/cache.py | 205 +- openerp/tools/import_email.py | 4 +- openerp/tools/misc.py | 17 + openerp/tools/test_reports.py | 2 +- openerp/tools/translate.py | 86 +- openerp/tools/yaml_import.py | 71 +- openerp/tools/yaml_tag.py | 1 + openerp/workflow/workitem.py | 7 +- setup.py | 2 + 426 files changed, 14918 insertions(+), 12581 deletions(-) delete mode 100644 addons/auth_crypt/i18n/base_crypt.pot delete mode 100644 addons/auth_crypt/i18n/bg.po delete mode 100644 addons/auth_crypt/i18n/ca.po delete mode 100644 addons/auth_crypt/i18n/el.po delete mode 100644 addons/auth_crypt/i18n/es_CL.po delete mode 100644 addons/auth_crypt/i18n/es_CR.po delete mode 100644 addons/auth_crypt/i18n/es_MX.po delete mode 100644 addons/auth_crypt/i18n/es_PY.po delete mode 100644 addons/auth_crypt/i18n/es_VE.po delete mode 100644 addons/auth_crypt/i18n/fa.po delete mode 100644 addons/auth_crypt/i18n/fi.po delete mode 100644 addons/auth_crypt/i18n/gu.po delete mode 100644 addons/auth_crypt/i18n/id.po delete mode 100644 addons/auth_crypt/i18n/ja.po delete mode 100644 addons/auth_crypt/i18n/lv.po delete mode 100644 addons/auth_crypt/i18n/nb.po delete mode 100644 addons/auth_crypt/i18n/oc.po delete mode 100644 addons/auth_crypt/i18n/sk.po delete mode 100644 addons/auth_crypt/i18n/sq.po delete mode 100644 addons/auth_crypt/i18n/sr@latin.po delete mode 100644 addons/auth_crypt/i18n/vi.po delete mode 100644 addons/auth_signup/signup.xml delete mode 100644 addons/calendar/base_calendar_view.xml delete mode 100644 addons/calendar/crm_meeting.py delete mode 100644 addons/calendar/crm_meeting_data.xml delete mode 100644 addons/google_drive/static/src/xml/gdocs.xml delete mode 100644 addons/hr/images/photo.png delete mode 100644 addons/l10n_uk/i18n/l10n_chart_uk_minimal.pot delete mode 100644 addons/mail/static/scripts/__init__.py delete mode 100644 addons/mail/static/scripts/openerp_mailgate/__init__.py delete mode 100644 addons/mrp/tests/__init__.py delete mode 100644 addons/procurement_jit/mrp_jit.xml delete mode 100644 addons/website_blog/wizard/document_page_create_menu.py delete mode 100644 addons/website_blog/wizard/document_page_create_menu_view.xml create mode 100644 doc/new_api.rst create mode 100644 openerp/addons/base/tests/test_api.py create mode 100644 openerp/addons/test_inherit/__init__.py create mode 100644 openerp/addons/test_inherit/__openerp__.py create mode 100644 openerp/addons/test_inherit/ir.model.access.csv create mode 100644 openerp/addons/test_inherit/models.py create mode 100644 openerp/addons/test_inherit/tests/__init__.py create mode 100644 openerp/addons/test_inherit/tests/test_inherit.py create mode 100644 openerp/addons/test_new_api/__init__.py create mode 100644 openerp/addons/test_new_api/__openerp__.py create mode 100644 openerp/addons/test_new_api/demo_data.xml create mode 100644 openerp/addons/test_new_api/ir.model.access.csv create mode 100644 openerp/addons/test_new_api/models.py create mode 100644 openerp/addons/test_new_api/tests/__init__.py create mode 100644 openerp/addons/test_new_api/tests/test_attributes.py create mode 100644 openerp/addons/test_new_api/tests/test_field_conversions.py create mode 100644 openerp/addons/test_new_api/tests/test_new_fields.py create mode 100644 openerp/addons/test_new_api/tests/test_onchange.py rename openerp/addons/{base/tests/test_fields.py => test_new_api/tests/test_related.py} (73%) create mode 100644 openerp/addons/test_new_api/views.xml create mode 100644 openerp/api.py create mode 100644 openerp/fields.py create mode 100644 openerp/models.py diff --git a/addons/account/account.py b/addons/account/account.py index 98ebcb54224..eeb915bddb0 100644 --- a/addons/account/account.py +++ b/addons/account/account.py @@ -26,7 +26,7 @@ from operator import itemgetter import time import openerp -from openerp import SUPERUSER_ID +from openerp import SUPERUSER_ID, api from openerp import tools from openerp.osv import fields, osv, expression from openerp.tools.translate import _ @@ -62,7 +62,7 @@ class account_payment_term(osv.osv): 'name': fields.char('Payment Term', translate=True, required=True), 'active': fields.boolean('Active', help="If the active field is set to False, it will allow you to hide the payment term without removing it."), 'note': fields.text('Description', translate=True), - 'line_ids': fields.one2many('account.payment.term.line', 'payment_id', 'Terms'), + 'line_ids': fields.one2many('account.payment.term.line', 'payment_id', 'Terms', copy=True), } _defaults = { 'active': 1, @@ -213,9 +213,6 @@ def _code_get(self, cr, uid, context=None): # Accounts #---------------------------------------------------------- -class account_tax(osv.osv): - _name = 'account.tax' - class account_account(osv.osv): _order = "parent_left" _parent_order = "code" @@ -640,16 +637,16 @@ class account_account(osv.osv): def _check_moves(self, cr, uid, ids, method, context=None): line_obj = self.pool.get('account.move.line') - account_ids = self.search(cr, uid, [('id', 'child_of', ids)]) + account_ids = self.search(cr, uid, [('id', 'child_of', ids)], context=context) - if line_obj.search(cr, uid, [('account_id', 'in', account_ids)]): + if line_obj.search(cr, uid, [('account_id', 'in', account_ids)], context=context): if method == 'write': raise osv.except_osv(_('Error!'), _('You cannot deactivate an account that contains journal items.')) elif method == 'unlink': raise osv.except_osv(_('Error!'), _('You cannot remove an account that contains journal items.')) #Checking whether the account is set as a property to any Partner or not - value = 'account.account,' + str(ids[0]) - partner_prop_acc = self.pool.get('ir.property').search(cr, uid, [('value_reference','=',value)], context=context) + values = ['account.account,%s' % (account_id,) for account_id in ids] + partner_prop_acc = self.pool.get('ir.property').search(cr, uid, [('value_reference','in', values)], context=context) if partner_prop_acc: raise osv.except_osv(_('Warning!'), _('You cannot remove/deactivate an account which is set on a customer or supplier.')) return True @@ -691,10 +688,10 @@ class account_account(osv.osv): # Dont allow changing the company_id when account_move_line already exist if 'company_id' in vals: - move_lines = self.pool.get('account.move.line').search(cr, uid, [('account_id', 'in', ids)]) + move_lines = self.pool.get('account.move.line').search(cr, uid, [('account_id', 'in', ids)], context=context) if move_lines: # Allow the write if the value is the same - for i in [i['company_id'][0] for i in self.read(cr,uid,ids,['company_id'])]: + for i in [i['company_id'][0] for i in self.read(cr,uid,ids,['company_id'], context=context)]: if vals['company_id']!=i: raise osv.except_osv(_('Warning!'), _('You cannot change the owner company of an account that already contains journal items.')) if 'active' in vals and not vals['active']: @@ -730,7 +727,7 @@ class account_journal(osv.osv): 'centralisation': fields.boolean('Centralized Counterpart', help="Check this box to determine that each entry of this journal won't create a new counterpart but will share the same counterpart. This is used in fiscal year closing."), 'update_posted': fields.boolean('Allow Cancelling Entries', help="Check this box if you want to allow the cancellation the entries related to this journal or of the invoice related to this journal"), 'group_invoice_lines': fields.boolean('Group Invoice Lines', help="If this box is checked, the system will try to group the accounting lines when generating them from invoices."), - 'sequence_id': fields.many2one('ir.sequence', 'Entry Sequence', help="This field contains the information related to the numbering of the journal entries of this journal.", required=True), + 'sequence_id': fields.many2one('ir.sequence', 'Entry Sequence', help="This field contains the information related to the numbering of the journal entries of this journal.", required=True, copy=False), 'user_id': fields.many2one('res.users', 'User', help="The user responsible for this journal"), 'groups_id': fields.many2many('res.groups', 'account_journal_group_rel', 'journal_id', 'group_id', 'Groups'), 'currency': fields.many2one('res.currency', 'Currency', help='The currency used to enter statement'), @@ -769,15 +766,12 @@ class account_journal(osv.osv): (_check_currency, 'Configuration error!\nThe currency chosen should be shared by the default accounts too.', ['currency','default_debit_account_id','default_credit_account_id']), ] - def copy(self, cr, uid, id, default=None, context=None, done_list=None, local=False): - default = {} if default is None else default.copy() - if done_list is None: - done_list = [] + def copy(self, cr, uid, id, default=None, context=None): + default = dict(context or {}) journal = self.browse(cr, uid, id, context=context) default.update( code=_("%s (copy)") % (journal['code'] or ''), - name=_("%s (copy)") % (journal['name'] or ''), - sequence_id=False) + name=_("%s (copy)") % (journal['name'] or '')) return super(account_journal, self).copy(cr, uid, id, default, context=context) def write(self, cr, uid, ids, vals, context=None): @@ -865,7 +859,10 @@ class account_fiscalyear(osv.osv): 'date_start': fields.date('Start Date', required=True), 'date_stop': fields.date('End Date', required=True), 'period_ids': fields.one2many('account.period', 'fiscalyear_id', 'Periods'), - 'state': fields.selection([('draft','Open'), ('done','Closed')], 'Status', readonly=True), + 'state': fields.selection([('draft','Open'), ('done','Closed')], 'Status', readonly=True, copy=False), + 'end_journal_period_id': fields.many2one( + 'account.journal.period', 'End of Year Entries Journal', + readonly=True, copy=False), } _defaults = { 'state': 'draft', @@ -960,7 +957,7 @@ class account_period(osv.osv): 'date_start': fields.date('Start of Period', required=True, states={'done':[('readonly',True)]}), 'date_stop': fields.date('End of Period', required=True, states={'done':[('readonly',True)]}), 'fiscalyear_id': fields.many2one('account.fiscalyear', 'Fiscal Year', required=True, states={'done':[('readonly',True)]}, select=True), - 'state': fields.selection([('draft','Open'), ('done','Closed')], 'Status', readonly=True, + 'state': fields.selection([('draft','Open'), ('done','Closed')], 'Status', readonly=True, copy=False, help='When monthly periods are created. The status is \'Draft\'. At the end of monthly period it is in \'Done\' status.'), 'company_id': fields.related('fiscalyear_id', 'company_id', type='many2one', relation='res.company', string='Company', store=True, readonly=True) } @@ -1000,12 +997,14 @@ class account_period(osv.osv): (_check_year_limit, 'Error!\nThe period is invalid. Either some periods are overlapping or the period\'s dates are not matching the scope of the fiscal year.', ['date_stop']) ] + @api.returns('self') def next(self, cr, uid, period, step, context=None): ids = self.search(cr, uid, [('date_start','>',period.date_start)]) if len(ids)>=step: return ids[step-1] return False + @api.returns('self') def find(self, cr, uid, dt=None, context=None): if context is None: context = {} if not dt: @@ -1028,13 +1027,14 @@ class account_period(osv.osv): raise openerp.exceptions.RedirectWarning(msg, action_id, _('Go to the configuration panel')) return result - def action_draft(self, cr, uid, ids, *args): + def action_draft(self, cr, uid, ids, context=None): mode = 'draft' for period in self.browse(cr, uid, ids): if period.fiscalyear_id.state == 'done': raise osv.except_osv(_('Warning!'), _('You can not re-open a period which belongs to closed fiscal year')) cr.execute('update account_journal_period set state=%s where period_id in %s', (mode, tuple(ids),)) cr.execute('update account_period set state=%s where id in %s', (mode, tuple(ids),)) + self.invalidate_cache(cr, uid, context=context) return True def name_search(self, cr, user, name, args=None, operator='ilike', context=None, limit=100): @@ -1132,22 +1132,6 @@ class account_journal_period(osv.osv): } _order = "period_id" - -class account_fiscalyear(osv.osv): - _inherit = "account.fiscalyear" - _description = "Fiscal Year" - _columns = { - 'end_journal_period_id':fields.many2one('account.journal.period','End of Year Entries Journal', readonly=True), - } - - def copy(self, cr, uid, id, default=None, context=None): - default = {} if default is None else default.copy() - default.update({ - 'period_ids': [], - 'end_journal_period_id': False - }) - return super(account_fiscalyear, self).copy(cr, uid, id, default=default, context=context) - #---------------------------------------------------------- # Entries #---------------------------------------------------------- @@ -1235,13 +1219,21 @@ class account_move(osv.osv): return [line.move_id.id for line in line_obj.browse(cr, uid, ids, context=context)] _columns = { - 'name': fields.char('Number', required=True), - 'ref': fields.char('Reference'), + 'name': fields.char('Number', required=True, copy=False), + 'ref': fields.char('Reference', copy=False), 'period_id': fields.many2one('account.period', 'Period', required=True, states={'posted':[('readonly',True)]}), 'journal_id': fields.many2one('account.journal', 'Journal', required=True, states={'posted':[('readonly',True)]}), - 'state': fields.selection([('draft','Unposted'), ('posted','Posted')], 'Status', required=True, readonly=True, - help='All manually created new journal entries are usually in the status \'Unposted\', but you can set the option to skip that status on the related journal. In that case, they will behave as journal entries automatically created by the system on document validation (invoices, bank statements...) and will be created in \'Posted\' status.'), - 'line_id': fields.one2many('account.move.line', 'move_id', 'Entries', states={'posted':[('readonly',True)]}), + 'state': fields.selection( + [('draft','Unposted'), ('posted','Posted')], 'Status', + required=True, readonly=True, copy=False, + help='All manually created new journal entries are usually in the status \'Unposted\', ' + 'but you can set the option to skip that status on the related journal. ' + 'In that case, they will behave as journal entries automatically created by the ' + 'system on document validation (invoices, bank statements...) and will be created ' + 'in \'Posted\' status.'), + 'line_id': fields.one2many('account.move.line', 'move_id', 'Entries', + states={'posted':[('readonly',True)]}, + copy=True), 'to_check': fields.boolean('To Review', help='Check this box if you are unsure of that journal entry and if you want to note it as \'to be reviewed\' by an accounting expert.'), 'partner_id': fields.related('line_id', 'partner_id', type="many2one", relation="res.partner", string="Partner", store={ _name: (lambda self, cr,uid,ids,c: ids, ['line_id'], 10), @@ -1309,6 +1301,7 @@ class account_move(osv.osv): 'SET state=%s '\ 'WHERE id IN %s', ('posted', tuple(valid_moves),)) + self.invalidate_cache(cr, uid, context=context) return True def button_validate(self, cursor, user, ids, context=None): @@ -1335,6 +1328,7 @@ class account_move(osv.osv): cr.execute('UPDATE account_move '\ 'SET state=%s '\ 'WHERE id IN %s', ('draft', tuple(ids),)) + self.invalidate_cache(cr, uid, context=context) return True def write(self, cr, uid, ids, vals, context=None): @@ -1350,23 +1344,9 @@ class account_move(osv.osv): # TODO: Check if period is closed ! # def create(self, cr, uid, vals, context=None): - if context is None: - context = {} - if 'line_id' in vals and context.get('copy'): - for l in vals['line_id']: - if not l[0]: - l[2].update({ - 'reconcile_id':False, - 'reconcile_partial_id':False, - 'analytic_lines':False, - 'invoice':False, - 'ref':False, - 'balance':False, - 'account_tax_id':False, - 'statement_id': False, - }) - - if 'journal_id' in vals and vals.get('journal_id', False): + context = dict(context or {}) + if vals.get('line_id'): + if vals.get('journal_id'): for l in vals['line_id']: if not l[0]: l[2]['journal_id'] = vals['journal_id'] @@ -1383,7 +1363,6 @@ class account_move(osv.osv): l[2]['period_id'] = default_period context['period_id'] = default_period - if vals.get('line_id', False): c = context.copy() c['novalidate'] = True c['period_id'] = vals['period_id'] if 'period_id' in vals else self._get_period(cr, uid, context) @@ -1398,22 +1377,8 @@ class account_move(osv.osv): result = super(account_move, self).create(cr, uid, vals, context) return result - def copy(self, cr, uid, id, default=None, context=None): - default = {} if default is None else default.copy() - context = {} if context is None else context.copy() - default.update({ - 'state':'draft', - 'ref': False, - 'name':'/', - }) - context.update({ - 'copy':True - }) - return super(account_move, self).copy(cr, uid, id, default, context) - def unlink(self, cr, uid, ids, context=None, check=True): - if context is None: - context = {} + context = dict(context or {}) if isinstance(ids, (int, long)): ids = [ids] toremove = [] @@ -1447,8 +1412,8 @@ class account_move(osv.osv): def _centralise(self, cr, uid, move, mode, context=None): assert mode in ('debit', 'credit'), 'Invalid Mode' #to prevent sql injection currency_obj = self.pool.get('res.currency') - if context is None: - context = {} + account_move_line_obj = self.pool.get('account.move.line') + context = dict(context or {}) if mode=='credit': account_id = move.journal_id.default_debit_account_id.id @@ -1473,7 +1438,7 @@ class account_move(osv.osv): line_id = res[0] else: context.update({'journal_id': move.journal_id.id, 'period_id': move.period_id.id}) - line_id = self.pool.get('account.move.line').create(cr, uid, { + line_id = account_move_line_obj.create(cr, uid, { 'name': _(mode.capitalize()+' Centralisation'), 'centralisation': mode, 'partner_id': False, @@ -1498,6 +1463,7 @@ class account_move(osv.osv): cr.execute('SELECT SUM(%s) FROM account_move_line WHERE move_id=%%s AND id!=%%s' % (mode,), (move.id, line_id2)) result = cr.fetchone()[0] or 0.0 cr.execute('update account_move_line set '+mode2+'=%s where id=%s', (result, line_id)) + account_move_line_obj.invalidate_cache(cr, uid, [mode2], [line_id], context=context) #adjust also the amount in currency if needed cr.execute("select currency_id, sum(amount_currency) as amount_currency from account_move_line where move_id = %s and currency_id is not null group by currency_id", (move.id,)) @@ -1510,9 +1476,10 @@ class account_move(osv.osv): res = cr.fetchone() if res: cr.execute('update account_move_line set amount_currency=%s , account_id=%s where id=%s', (amount_currency, account_id, res[0])) + account_move_line_obj.invalidate_cache(cr, uid, ['amount_currency', 'account_id'], [res[0]], context=context) else: context.update({'journal_id': move.journal_id.id, 'period_id': move.period_id.id}) - line_id = self.pool.get('account.move.line').create(cr, uid, { + line_id = account_move_line_obj.create(cr, uid, { 'name': _('Currency Adjustment'), 'centralisation': 'currency', 'partner_id': False, @@ -1818,7 +1785,7 @@ class account_tax_code(osv.osv): return [] if isinstance(ids, (int, long)): ids = [ids] - reads = self.read(cr, uid, ids, ['name','code'], context, load='_classic_write') + reads = self.read(cr, uid, ids, ['name','code'], context=context, load='_classic_write') return [(x['id'], (x['code'] and (x['code'] + ' - ') or '') + x['name']) \ for x in reads] @@ -1827,19 +1794,13 @@ class account_tax_code(osv.osv): if user.company_id: return user.company_id.id return self.pool.get('res.company').search(cr, uid, [('parent_id', '=', False)])[0] + _defaults = { 'company_id': _default_company, 'sign': 1.0, 'notprintable': False, } - def copy(self, cr, uid, id, default=None, context=None): - if default is None: - default = {} - default = default.copy() - default.update({'line_ids': []}) - return super(account_tax_code, self).copy(cr, uid, id, default, context) - _check_recursion = check_cycle _constraints = [ (_check_recursion, 'Error!\nYou cannot create recursive accounts.', ['parent_id']) @@ -1868,10 +1829,9 @@ class account_tax(osv.osv): def copy_data(self, cr, uid, id, default=None, context=None): if default is None: default = {} - name = self.read(cr, uid, id, ['name'], context=context)['name'] - default = default.copy() - default.update({'name': name + _(' (Copy)')}) - return super(account_tax, self).copy_data(cr, uid, id, default=default, context=context) + this = self.browse(cr, uid, id, context=context) + tmp_default = dict(default, name=_("%s (Copy)") % this.name) + return super(account_tax, self).copy_data(cr, uid, id, default=tmp_default, context=context) _name = 'account.tax' _description = 'Tax' @@ -2087,6 +2047,7 @@ class account_tax(osv.osv): tax = self.browse(cr, uid, tax_id, context=context) return self.compute_all(cr, uid, [tax], amount, 1) # TOCHECK may use force_exclude parameter + @api.v7 def compute_all(self, cr, uid, taxes, price_unit, quantity, product=None, partner=None, force_excluded=False): """ :param force_excluded: boolean used to say that we don't want to consider the value of field price_include of @@ -2137,6 +2098,12 @@ class account_tax(osv.osv): 'taxes': tin + tex } + @api.v8 + def compute_all(self, price_unit, quantity, product=None, partner=None, force_excluded=False): + return self._model.compute_all( + self._cr, self._uid, self, price_unit, quantity, + product=product, partner=partner, force_excluded=force_excluded) + def compute(self, cr, uid, taxes, price_unit, quantity, product=None, partner=None): _logger.warning("Deprecated, use compute_all(...)['taxes'] instead of compute(...) to manage prices with tax included.") return self._compute(cr, uid, taxes, price_unit, quantity, product, partner) @@ -2270,7 +2237,7 @@ class account_model(osv.osv): 'name': fields.char('Model Name', required=True, help="This is a model for recurring accounting entries"), 'journal_id': fields.many2one('account.journal', 'Journal', required=True), 'company_id': fields.related('journal_id', 'company_id', type='many2one', relation='res.company', string='Company', store=True, readonly=True), - 'lines_id': fields.one2many('account.model.line', 'model_id', 'Model Entries'), + 'lines_id': fields.one2many('account.model.line', 'model_id', 'Model Entries', copy=True), 'legend': fields.text('Legend', readonly=True, size=100), } @@ -2292,6 +2259,7 @@ class account_model(osv.osv): context = {} if data.get('date', False): + context = dict(context) context.update({'date': data['date']}) move_date = context.get('date', time.strftime('%Y-%m-%d')) @@ -2409,8 +2377,8 @@ class account_subscription(osv.osv): 'period_total': fields.integer('Number of Periods', required=True), 'period_nbr': fields.integer('Period', required=True), 'period_type': fields.selection([('day','days'),('month','month'),('year','year')], 'Period Type', required=True), - 'state': fields.selection([('draft','Draft'),('running','Running'),('done','Done')], 'Status', required=True, readonly=True), - 'lines_id': fields.one2many('account.subscription.line', 'subscription_id', 'Subscription Lines') + 'state': fields.selection([('draft','Draft'),('running','Running'),('done','Done')], 'Status', required=True, readonly=True, copy=False), + 'lines_id': fields.one2many('account.subscription.line', 'subscription_id', 'Subscription Lines', copy=True) } _defaults = { 'date_start': fields.date.context_today, @@ -2742,7 +2710,7 @@ class account_tax_code_template(osv.osv): return [] if isinstance(ids, (int, long)): ids = [ids] - reads = self.read(cr, uid, ids, ['name','code'], context, load='_classic_write') + reads = self.read(cr, uid, ids, ['name','code'], context=context, load='_classic_write') return [(x['id'], (x['code'] and x['code'] + ' - ' or '') + x['name']) \ for x in reads] diff --git a/addons/account/account_bank_statement.py b/addons/account/account_bank_statement.py index 3320ee83162..98e80f895f4 100644 --- a/addons/account/account_bank_statement.py +++ b/addons/account/account_bank_statement.py @@ -69,8 +69,7 @@ class account_bank_statement(osv.osv): return False def _compute_default_statement_name(self, cr, uid, journal_id, context=None): - if context is None: - context = {} + context = dict(context or {}) obj_seq = self.pool.get('ir.sequence') period = self.pool.get('account.period').browse(cr, uid, self._get_period(cr, uid, context=context), context=context) context['fiscalyear_id'] = period.fiscalyear_id.id @@ -114,8 +113,16 @@ class account_bank_statement(osv.osv): _description = "Bank Statement" _inherit = ['mail.thread'] _columns = { - 'name': fields.char('Reference', states={'draft': [('readonly', False)]}, readonly=True, help='if you give the Name other then /, its created Accounting Entries Move will be with same name as statement name. This allows the statement entries to have the same references than the statement itself'), # readonly for account_cash_statement - 'date': fields.date('Date', required=True, states={'confirm': [('readonly', True)]}, select=True), + 'name': fields.char( + 'Reference', states={'draft': [('readonly', False)]}, + readonly=True, # readonly for account_cash_statement + copy=False, + help='if you give the Name other then /, its created Accounting Entries Move ' + 'will be with same name as statement name. ' + 'This allows the statement entries to have the same references than the ' + 'statement itself'), + 'date': fields.date('Date', required=True, states={'confirm': [('readonly', True)]}, + select=True, copy=False), 'journal_id': fields.many2one('account.journal', 'Journal', required=True, readonly=True, states={'draft':[('readonly',False)]}), 'period_id': fields.many2one('account.period', 'Period', required=True, @@ -132,14 +139,15 @@ class account_bank_statement(osv.osv): string="Computed Balance", help='Balance as calculated based on Opening Balance and transaction lines'), 'company_id': fields.related('journal_id', 'company_id', type='many2one', relation='res.company', string='Company', store=True, readonly=True), 'line_ids': fields.one2many('account.bank.statement.line', - 'statement_id', 'Statement lines', - states={'confirm':[('readonly', True)]}), + 'statement_id', 'Statement lines', + states={'confirm':[('readonly', True)]}, copy=True), 'move_line_ids': fields.one2many('account.move.line', 'statement_id', - 'Entry lines', states={'confirm':[('readonly',True)]}), + 'Entry lines', states={'confirm':[('readonly',True)]}), 'state': fields.selection([('draft', 'New'), ('open','Open'), # used by cash statements ('confirm', 'Closed')], 'Status', required=True, readonly="1", + copy=False, help='When new statement is created the status will be \'Draft\'.\n' 'And after getting confirmation from the bank it will be in \'Confirmed\' status.'), 'currency': fields.function(_currency, string='Currency', @@ -182,7 +190,7 @@ class account_bank_statement(osv.osv): pids = period_pool.find(cr, uid, dt=date, context=ctx) if pids: res.update({'period_id': pids[0]}) - context.update({'period_id': pids[0]}) + context = dict(context, period_id=pids[0]) return { 'value':res, @@ -363,24 +371,13 @@ class account_bank_statement(osv.osv): return {'value': res} def unlink(self, cr, uid, ids, context=None): - stat = self.read(cr, uid, ids, ['state'], context=context) - unlink_ids = [] - for t in stat: - if t['state'] in ('draft'): - unlink_ids.append(t['id']) - else: - raise osv.except_osv(_('Invalid Action!'), _('In order to delete a bank statement, you must first cancel it to delete related journal items.')) - osv.osv.unlink(self, cr, uid, unlink_ids, context=context) - return True - - def copy(self, cr, uid, id, default=None, context=None): - if default is None: - default = {} - if context is None: - context = {} - default = default.copy() - default['move_line_ids'] = [] - return super(account_bank_statement, self).copy(cr, uid, id, default, context=context) + for item in self.browse(cr, uid, ids, context=context): + if item.state != 'draft': + raise osv.except_osv( + _('Invalid Action!'), + _('In order to delete a bank statement, you must first cancel it to delete related journal items.') + ) + return super(account_bank_statement, self).unlink(cr, uid, ids, context=context) def button_journal_entries(self, cr, uid, ids, context=None): ctx = (context or {}).copy() @@ -806,8 +803,8 @@ class account_bank_statement_line(osv.osv): _description = "Bank Statement Line" _inherit = ['ir.needaction_mixin'] _columns = { - 'name': fields.char('Description', required=True), - 'date': fields.date('Date', required=True), + 'name': fields.char('Description', required=True, copy=False), + 'date': fields.date('Date', required=True, copy=False), 'amount': fields.float('Amount', digits_compute=dp.get_precision('Account')), 'partner_id': fields.many2one('res.partner', 'Partner'), 'bank_account_id': fields.many2one('res.partner.bank','Bank Account'), diff --git a/addons/account/account_cash_statement.py b/addons/account/account_cash_statement.py index c1c5265d8c2..5c6e4ef4292 100644 --- a/addons/account/account_cash_statement.py +++ b/addons/account/account_cash_statement.py @@ -179,7 +179,7 @@ class account_cash_statement(osv.osv): }, help="Total of cash transaction lines."), 'closing_date': fields.datetime("Closed On"), - 'details_ids' : fields.one2many('account.cashbox.line', 'bank_statement_id', string='CashBox Lines'), + 'details_ids' : fields.one2many('account.cashbox.line', 'bank_statement_id', string='CashBox Lines', copy=True), 'opening_details_ids' : fields.one2many('account.cashbox.line', 'bank_statement_id', string='Opening Cashbox Lines'), 'closing_details_ids' : fields.one2many('account.cashbox.line', 'bank_statement_id', string='Closing Cashbox Lines'), 'user_id': fields.many2one('res.users', 'Responsible', required=False), @@ -337,7 +337,7 @@ class account_journal(osv.osv): return result _columns = { - 'cashbox_line_ids' : fields.one2many('account.journal.cashbox.line', 'journal_id', 'CashBox'), + 'cashbox_line_ids' : fields.one2many('account.journal.cashbox.line', 'journal_id', 'CashBox', copy=True), } _defaults = { diff --git a/addons/account/account_invoice.py b/addons/account/account_invoice.py index 18773648e14..ad99ae40058 100644 --- a/addons/account/account_invoice.py +++ b/addons/account/account_invoice.py @@ -19,203 +19,36 @@ # ############################################################################## -import time +import itertools from lxml import etree + +from openerp import models, fields, api, _ +from openerp.exceptions import except_orm, Warning, RedirectWarning import openerp.addons.decimal_precision as dp -import openerp.exceptions -from openerp.osv import fields, osv -from openerp.tools.translate import _ +# mapping invoice type to journal type +TYPE2JOURNAL = { + 'out_invoice': 'sale', + 'in_invoice': 'purchase', + 'out_refund': 'sale_refund', + 'in_refund': 'purchase_refund', +} -class account_invoice(osv.osv): - def _amount_all(self, cr, uid, ids, name, args, context=None): - res = {} - for invoice in self.browse(cr, uid, ids, context=context): - res[invoice.id] = { - 'amount_untaxed': 0.0, - 'amount_tax': 0.0, - 'amount_total': 0.0 - } - for line in invoice.invoice_line: - res[invoice.id]['amount_untaxed'] += line.price_subtotal - for line in invoice.tax_line: - res[invoice.id]['amount_tax'] += line.amount - res[invoice.id]['amount_total'] = res[invoice.id]['amount_tax'] + res[invoice.id]['amount_untaxed'] - return res +# mapping invoice type to refund type +TYPE2REFUND = { + 'out_invoice': 'out_refund', # Customer Invoice + 'in_invoice': 'in_refund', # Supplier Invoice + 'out_refund': 'out_invoice', # Customer Refund + 'in_refund': 'in_invoice', # Supplier Refund +} - def _get_journal(self, cr, uid, context=None): - if context is None: - context = {} - type_inv = context.get('type', 'out_invoice') - user = self.pool.get('res.users').browse(cr, uid, uid, context=context) - company_id = context.get('company_id', user.company_id.id) - type2journal = {'out_invoice': 'sale', 'in_invoice': 'purchase', 'out_refund': 'sale_refund', 'in_refund': 'purchase_refund'} - journal_obj = self.pool.get('account.journal') - domain = [('company_id', '=', company_id)] - if isinstance(type_inv, list): - domain.append(('type', 'in', [type2journal.get(type) for type in type_inv if type2journal.get(type)])) - else: - domain.append(('type', '=', type2journal.get(type_inv, 'sale'))) - res = journal_obj.search(cr, uid, domain, limit=1) - return res and res[0] or False +MAGIC_COLUMNS = ('id', 'create_uid', 'create_date', 'write_uid', 'write_date') - def _get_currency(self, cr, uid, context=None): - res = False - journal_id = self._get_journal(cr, uid, context=context) - if journal_id: - journal = self.pool.get('account.journal').browse(cr, uid, journal_id, context=context) - res = journal.currency and journal.currency.id or journal.company_id.currency_id.id - return res - - def _get_journal_analytic(self, cr, uid, type_inv, context=None): - type2journal = {'out_invoice': 'sale', 'in_invoice': 'purchase', 'out_refund': 'sale', 'in_refund': 'purchase'} - tt = type2journal.get(type_inv, 'sale') - result = self.pool.get('account.analytic.journal').search(cr, uid, [('type','=',tt)], context=context) - if not result: - raise osv.except_osv(_('No Analytic Journal!'),_("You must define an analytic journal of type '%s'!") % (tt,)) - return result[0] - - def _get_type(self, cr, uid, context=None): - if context is None: - context = {} - return context.get('type', 'out_invoice') - - def _reconciled(self, cr, uid, ids, name, args, context=None): - res = {} - for inv in self.browse(cr, uid, ids, context=context): - res[inv.id] = self.test_paid(cr, uid, [inv.id]) - if not res[inv.id] and inv.state == 'paid': - self.signal_open_test(cr, uid, [inv.id]) - return res - - def _get_reference_type(self, cr, uid, context=None): - return [('none', _('Free Reference'))] - - def _amount_residual(self, cr, uid, ids, name, args, context=None): - """Function of the field residua. It computes the residual amount (balance) for each invoice""" - if context is None: - context = {} - ctx = context.copy() - result = {} - currency_obj = self.pool.get('res.currency') - for invoice in self.browse(cr, uid, ids, context=context): - nb_inv_in_partial_rec = max_invoice_id = 0 - result[invoice.id] = 0.0 - if invoice.move_id: - for aml in invoice.move_id.line_id: - if aml.account_id.type in ('receivable','payable'): - if aml.currency_id and aml.currency_id.id == invoice.currency_id.id: - result[invoice.id] += aml.amount_residual_currency - else: - ctx['date'] = aml.date - result[invoice.id] += currency_obj.compute(cr, uid, aml.company_id.currency_id.id, invoice.currency_id.id, aml.amount_residual, context=ctx) - - if aml.reconcile_partial_id.line_partial_ids: - #we check if the invoice is partially reconciled and if there are other invoices - #involved in this partial reconciliation (and we sum these invoices) - for line in aml.reconcile_partial_id.line_partial_ids: - if line.invoice and invoice.type == line.invoice.type: - nb_inv_in_partial_rec += 1 - #store the max invoice id as for this invoice we will make a balance instead of a simple division - max_invoice_id = max(max_invoice_id, line.invoice.id) - if nb_inv_in_partial_rec: - #if there are several invoices in a partial reconciliation, we split the residual by the number - #of invoice to have a sum of residual amounts that matches the partner balance - new_value = currency_obj.round(cr, uid, invoice.currency_id, result[invoice.id] / nb_inv_in_partial_rec) - if invoice.id == max_invoice_id: - #if it's the last the invoice of the bunch of invoices partially reconciled together, we make a - #balance to avoid rounding errors - result[invoice.id] = result[invoice.id] - ((nb_inv_in_partial_rec - 1) * new_value) - else: - result[invoice.id] = new_value - - #prevent the residual amount on the invoice to be less than 0 - result[invoice.id] = max(result[invoice.id], 0.0) - return result - - # Give Journal Items related to the payment reconciled to this invoice - # Return ids of partial and total payments related to the selected invoices - def _get_lines(self, cr, uid, ids, name, arg, context=None): - res = {} - for invoice in self.browse(cr, uid, ids, context=context): - id = invoice.id - res[id] = [] - if not invoice.move_id: - continue - data_lines = [x for x in invoice.move_id.line_id if x.account_id.id == invoice.account_id.id] - partial_ids = [] - for line in data_lines: - ids_line = [] - if line.reconcile_id: - ids_line = line.reconcile_id.line_id - elif line.reconcile_partial_id: - ids_line = line.reconcile_partial_id.line_partial_ids - l = map(lambda x: x.id, ids_line) - partial_ids.append(line.id) - res[id] =[x for x in l if x <> line.id and x not in partial_ids] - return res - - def _get_invoice_line(self, cr, uid, ids, context=None): - result = {} - for line in self.pool.get('account.invoice.line').browse(cr, uid, ids, context=context): - result[line.invoice_id.id] = True - return result.keys() - - def _get_invoice_tax(self, cr, uid, ids, context=None): - result = {} - for tax in self.pool.get('account.invoice.tax').browse(cr, uid, ids, context=context): - result[tax.invoice_id.id] = True - return result.keys() - - def _compute_lines(self, cr, uid, ids, name, args, context=None): - result = {} - for invoice in self.browse(cr, uid, ids, context=context): - src = [] - lines = [] - if invoice.move_id: - for m in invoice.move_id.line_id: - temp_lines = [] - if m.reconcile_id: - temp_lines = map(lambda x: x.id, m.reconcile_id.line_id) - elif m.reconcile_partial_id: - temp_lines = map(lambda x: x.id, m.reconcile_partial_id.line_partial_ids) - lines += [x for x in temp_lines if x not in lines] - src.append(m.id) - - lines = filter(lambda x: x not in src, lines) - result[invoice.id] = lines - return result - - def _get_invoice_from_line(self, cr, uid, ids, context=None): - move = {} - for line in self.pool.get('account.move.line').browse(cr, uid, ids, context=context): - if line.reconcile_partial_id: - for line2 in line.reconcile_partial_id.line_partial_ids: - move[line2.move_id.id] = True - if line.reconcile_id: - for line2 in line.reconcile_id.line_id: - move[line2.move_id.id] = True - invoice_ids = [] - if move: - invoice_ids = self.pool.get('account.invoice').search(cr, uid, [('move_id','in',move.keys())], context=context) - return invoice_ids - - def _get_invoice_from_reconcile(self, cr, uid, ids, context=None): - move = {} - for r in self.pool.get('account.move.reconcile').browse(cr, uid, ids, context=context): - for line in r.line_partial_ids: - move[line.move_id.id] = True - for line in r.line_id: - move[line.move_id.id] = True - - invoice_ids = [] - if move: - invoice_ids = self.pool.get('account.invoice').search(cr, uid, [('move_id','in',move.keys())], context=context) - return invoice_ids +class account_invoice(models.Model): _name = "account.invoice" _inherit = ['mail.thread'] - _description = 'Invoice' + _description = "Invoice" _order = "number desc, id desc" _track = { 'type': { @@ -225,670 +58,713 @@ class account_invoice(osv.osv): 'account.mt_invoice_validated': lambda self, cr, uid, obj, ctx=None: obj.state == 'open' and obj.type in ('out_invoice', 'out_refund'), }, } - _columns = { - 'name': fields.char('Reference/Description', select=True, readonly=True, states={'draft':[('readonly',False)]}), - 'origin': fields.char('Source Document', help="Reference of the document that produced this invoice.", readonly=True, states={'draft':[('readonly',False)]}), - 'supplier_invoice_number': fields.char('Supplier Invoice Number', size=64, help="The reference of this invoice as provided by the supplier.", readonly=True, states={'draft':[('readonly',False)]}), - 'type': fields.selection([ + + @api.one + @api.depends('invoice_line.price_subtotal', 'tax_line.amount') + def _compute_amount(self): + self.amount_untaxed = sum(line.price_subtotal for line in self.invoice_line) + self.amount_tax = sum(line.amount for line in self.tax_line) + self.amount_total = self.amount_untaxed + self.amount_tax + + @api.model + def _default_journal(self): + inv_type = self._context.get('type', 'out_invoice') + inv_types = inv_type if isinstance(inv_type, list) else [inv_type] + company_id = self._context.get('company_id', self.env.user.company_id.id) + domain = [ + ('type', 'in', filter(None, map(TYPE2JOURNAL.get, inv_types))), + ('company_id', '=', company_id), + ] + return self.env['account.journal'].search(domain, limit=1) + + @api.model + def _default_currency(self): + journal = self._default_journal() + return journal.currency or journal.company_id.currency_id + + @api.model + @api.returns('account.analytic.journal') + def _get_journal_analytic(self, inv_type): + """ Return the analytic journal corresponding to the given invoice type. """ + journal_type = TYPE2JOURNAL.get(inv_type, 'sale') + journal = self.env['account.analytic.journal'].search([('type', '=', journal_type)], limit=1) + if not journal: + raise except_orm(_('No Analytic Journal!'), + _("You must define an analytic journal of type '%s'!") % (journal_type,)) + return journal + + @api.one + @api.depends('account_id', 'move_id.line_id.account_id', 'move_id.line_id.reconcile_id') + def _compute_reconciled(self): + self.reconciled = self.test_paid() + if not self.reconciled and self.state == 'paid': + self.signal_open_test() + + @api.model + def _get_reference_type(self): + return [('none', _('Free Reference'))] + + @api.one + @api.depends( + 'state', 'currency_id', 'invoice_line.price_subtotal', + 'move_id.line_id.account_id.type', + 'move_id.line_id.amount_residual', + 'move_id.line_id.amount_residual_currency', + 'move_id.line_id.currency_id', + 'move_id.line_id.reconcile_partial_id.line_partial_ids.invoice.type', + ) + def _compute_residual(self): + nb_inv_in_partial_rec = max_invoice_id = 0 + self.residual = 0.0 + for line in self.move_id.line_id: + if line.account_id.type in ('receivable', 'payable'): + if line.currency_id == self.currency_id: + self.residual += line.amount_residual_currency + else: + # ahem, shouldn't we use line.currency_id here? + from_currency = line.company_id.currency_id.with_context(date=line.date) + self.residual += from_currency.compute(line.amount_residual, self.currency_id) + # we check if the invoice is partially reconciled and if there + # are other invoices involved in this partial reconciliation + for pline in line.reconcile_partial_id.line_partial_ids: + if pline.invoice and self.type == pline.invoice.type: + nb_inv_in_partial_rec += 1 + # store the max invoice id as for this invoice we will + # make a balance instead of a simple division + max_invoice_id = max(max_invoice_id, pline.invoice.id) + if nb_inv_in_partial_rec: + # if there are several invoices in a partial reconciliation, we + # split the residual by the number of invoices to have a sum of + # residual amounts that matches the partner balance + new_value = self.currency_id.round(self.residual / nb_inv_in_partial_rec) + if self.id == max_invoice_id: + # if it's the last the invoice of the bunch of invoices + # partially reconciled together, we make a balance to avoid + # rounding errors + self.residual = self.residual - ((nb_inv_in_partial_rec - 1) * new_value) + else: + self.residual = new_value + # prevent the residual amount on the invoice to be less than 0 + self.residual = max(self.residual, 0.0) + + @api.one + @api.depends( + 'move_id.line_id.account_id', + 'move_id.line_id.reconcile_id.line_id', + 'move_id.line_id.reconcile_partial_id.line_partial_ids', + ) + def _compute_move_lines(self): + # Give Journal Items related to the payment reconciled to this invoice. + # Return partial and total payments related to the selected invoice. + self.move_lines = self.env['account.move.line'] + if not self.move_id: + return + data_lines = self.move_id.line_id.filtered(lambda l: l.account_id == self.account_id) + partial_lines = self.env['account.move.line'] + for data_line in data_lines: + if data_line.reconcile_id: + lines = data_line.reconcile_id.line_id + elif data_line.reconcile_partial_id: + lines = data_line.reconcile_partial_id.line_partial_ids + else: + lines = self.env['account_move_line'] + partial_lines += data_line + self.move_lines = lines - partial_lines + + @api.one + @api.depends( + 'move_id.line_id.reconcile_id.line_id', + 'move_id.line_id.reconcile_partial_id.line_partial_ids', + ) + def _compute_payments(self): + partial_lines = lines = self.env['account.move.line'] + for line in self.move_id.line_id: + if line.reconcile_id: + lines |= line.reconcile_id.line_id + elif line.reconcile_partial_id: + lines |= line.reconcile_partial_id.line_partial_ids + partial_lines += line + self.payment_ids = (lines - partial_lines).sorted() + + name = fields.Char(string='Reference/Description', index=True, + readonly=True, states={'draft': [('readonly', False)]}) + origin = fields.Char(string='Source Document', + help="Reference of the document that produced this invoice.", + readonly=True, states={'draft': [('readonly', False)]}) + supplier_invoice_number = fields.Char(string='Supplier Invoice Number', + help="The reference of this invoice as provided by the supplier.", + readonly=True, states={'draft': [('readonly', False)]}) + type = fields.Selection([ ('out_invoice','Customer Invoice'), ('in_invoice','Supplier Invoice'), ('out_refund','Customer Refund'), ('in_refund','Supplier Refund'), - ],'Type', readonly=True, select=True, change_default=True, track_visibility='always'), + ], string='Type', readonly=True, index=True, change_default=True, + default=lambda self: self._context.get('type', 'out_invoice'), + track_visibility='always') - 'number': fields.related('move_id','name', type='char', readonly=True, size=64, relation='account.move', store=True, string='Number'), - 'internal_number': fields.char('Invoice Number', readonly=True, help="Unique number of the invoice, computed automatically when the invoice is created."), - 'reference': fields.char('Invoice Reference', help="The partner reference of this invoice."), - 'reference_type': fields.selection(_get_reference_type, 'Payment Reference', - required=True, readonly=True, states={'draft':[('readonly',False)]}), - 'comment': fields.text('Additional Information'), + number = fields.Char(related='move_id.name', store=True, readonly=True, copy=False) + internal_number = fields.Char(string='Invoice Number', readonly=True, + default=False, copy=False, + help="Unique number of the invoice, computed automatically when the invoice is created.") + reference = fields.Char(string='Invoice Reference', + help="The partner reference of this invoice.") + reference_type = fields.Selection('_get_reference_type', string='Payment Reference', + required=True, readonly=True, states={'draft': [('readonly', False)]}, + default='none') + comment = fields.Text('Additional Information') - 'state': fields.selection([ + state = fields.Selection([ ('draft','Draft'), ('proforma','Pro-forma'), ('proforma2','Pro-forma'), ('open','Open'), ('paid','Paid'), ('cancel','Cancelled'), - ],'Status', select=True, readonly=True, track_visibility='onchange', - help=' * The \'Draft\' status is used when a user is encoding a new and unconfirmed Invoice. \ - \n* The \'Pro-forma\' when invoice is in Pro-forma status,invoice does not have an invoice number. \ - \n* The \'Open\' status is used when user create invoice,a invoice number is generated.Its in open status till user does not pay invoice. \ - \n* The \'Paid\' status is set automatically when the invoice is paid. Its related journal entries may or may not be reconciled. \ - \n* The \'Cancelled\' status is used when user cancel invoice.'), - 'sent': fields.boolean('Sent', readonly=True, help="It indicates that the invoice has been sent."), - 'date_invoice': fields.date('Invoice Date', readonly=True, states={'draft':[('readonly',False)]}, select=True, help="Keep empty to use the current date"), - 'date_due': fields.date('Due Date', readonly=True, states={'draft':[('readonly',False)]}, select=True, - help="If you use payment terms, the due date will be computed automatically at the generation "\ - "of accounting entries. The payment term may compute several due dates, for example 50% now and 50% in one month, but if you want to force a due date, make sure that the payment term is not set on the invoice. If you keep the payment term and the due date empty, it means direct payment."), - 'partner_id': fields.many2one('res.partner', 'Partner', change_default=True, readonly=True, required=True, states={'draft':[('readonly',False)]}, track_visibility='always'), - 'payment_term': fields.many2one('account.payment.term', 'Payment Terms',readonly=True, states={'draft':[('readonly',False)]}, - help="If you use payment terms, the due date will be computed automatically at the generation "\ - "of accounting entries. If you keep the payment term and the due date empty, it means direct payment. "\ - "The payment term may compute several due dates, for example 50% now, 50% in one month."), - 'period_id': fields.many2one('account.period', 'Force Period', domain=[('state','<>','done')], help="Keep empty to use the period of the validation(invoice) date.", readonly=True, states={'draft':[('readonly',False)]}), + ], string='Status', index=True, readonly=True, default='draft', + track_visibility='onchange', copy=False, + help=" * The 'Draft' status is used when a user is encoding a new and unconfirmed Invoice.\n" + " * The 'Pro-forma' when invoice is in Pro-forma status,invoice does not have an invoice number.\n" + " * The 'Open' status is used when user create invoice,a invoice number is generated.Its in open status till user does not pay invoice.\n" + " * The 'Paid' status is set automatically when the invoice is paid. Its related journal entries may or may not be reconciled.\n" + " * The 'Cancelled' status is used when user cancel invoice.") + sent = fields.Boolean(readonly=True, default=False, copy=False, + help="It indicates that the invoice has been sent.") + date_invoice = fields.Date(string='Invoice Date', + readonly=True, states={'draft': [('readonly', False)]}, index=True, + help="Keep empty to use the current date", copy=False) + date_due = fields.Date(string='Due Date', + readonly=True, states={'draft': [('readonly', False)]}, index=True, copy=False, + help="If you use payment terms, the due date will be computed automatically at the generation " + "of accounting entries. The payment term may compute several due dates, for example 50% " + "now and 50% in one month, but if you want to force a due date, make sure that the payment " + "term is not set on the invoice. If you keep the payment term and the due date empty, it " + "means direct payment.") + partner_id = fields.Many2one('res.partner', string='Partner', change_default=True, + required=True, readonly=True, states={'draft': [('readonly', False)]}, + track_visibility='always') + payment_term = fields.Many2one('account.payment.term', string='Payment Terms', + readonly=True, states={'draft': [('readonly', False)]}, + help="If you use payment terms, the due date will be computed automatically at the generation " + "of accounting entries. If you keep the payment term and the due date empty, it means direct payment. " + "The payment term may compute several due dates, for example 50% now, 50% in one month.") + period_id = fields.Many2one('account.period', string='Force Period', + domain=[('state', '!=', 'done')], copy=False, + help="Keep empty to use the period of the validation(invoice) date.", + readonly=True, states={'draft': [('readonly', False)]}) - 'account_id': fields.many2one('account.account', 'Account', required=True, readonly=True, states={'draft':[('readonly',False)]}, help="The partner account used for this invoice."), - 'invoice_line': fields.one2many('account.invoice.line', 'invoice_id', 'Invoice Lines', readonly=True, states={'draft':[('readonly',False)]}), - 'tax_line': fields.one2many('account.invoice.tax', 'invoice_id', 'Tax Lines', readonly=True, states={'draft':[('readonly',False)]}), + account_id = fields.Many2one('account.account', string='Account', + required=True, readonly=True, states={'draft': [('readonly', False)]}, + help="The partner account used for this invoice.") + invoice_line = fields.One2many('account.invoice.line', 'invoice_id', string='Invoice Lines', + readonly=True, states={'draft': [('readonly', False)]}, copy=True) + tax_line = fields.One2many('account.invoice.tax', 'invoice_id', string='Tax Lines', + readonly=True, states={'draft': [('readonly', False)]}, copy=True) + move_id = fields.Many2one('account.move', string='Journal Entry', + readonly=True, index=True, ondelete='restrict', copy=False, + help="Link to the automatically generated Journal Items.") + + amount_untaxed = fields.Float(string='Subtotal', digits=dp.get_precision('Account'), + store=True, readonly=True, compute='_compute_amount', track_visibility='always') + amount_tax = fields.Float(string='Tax', digits=dp.get_precision('Account'), + store=True, readonly=True, compute='_compute_amount') + amount_total = fields.Float(string='Total', digits=dp.get_precision('Account'), + store=True, readonly=True, compute='_compute_amount') + + currency_id = fields.Many2one('res.currency', string='Currency', + required=True, readonly=True, states={'draft': [('readonly', False)]}, + default=_default_currency, track_visibility='always') + journal_id = fields.Many2one('account.journal', string='Journal', + required=True, readonly=True, states={'draft': [('readonly', False)]}, + default=_default_journal, + domain="[('type', 'in', {'out_invoice': ['sale'], 'out_refund': ['sale_refund'], 'in_refund': ['purchase_refund'], 'in_invoice': ['purchase']}.get(type, [])), ('company_id', '=', company_id)]") + company_id = fields.Many2one('res.company', string='Company', change_default=True, + required=True, readonly=True, states={'draft': [('readonly', False)]}, + default=lambda self: self.env['res.company']._company_default_get('account.invoice')) + check_total = fields.Float(string='Verification Total', digits=dp.get_precision('Account'), + readonly=True, states={'draft': [('readonly', False)]}, default=0.0) + + reconciled = fields.Boolean(string='Paid/Reconciled', + store=True, readonly=True, compute='_compute_reconciled', + help="It indicates that the invoice has been paid and the journal entry of the invoice has been reconciled with one or several journal entries of payment.") + partner_bank_id = fields.Many2one('res.partner.bank', string='Bank Account', + help='Bank Account Number to which the invoice will be paid. A Company bank account if this is a Customer Invoice or Supplier Refund, otherwise a Partner bank account number.', + readonly=True, states={'draft': [('readonly', False)]}) + + move_lines = fields.Many2many('account.move.line', string='Entry Lines', + compute='_compute_move_lines') + residual = fields.Float(string='Balance', digits=dp.get_precision('Account'), + compute='_compute_residual', store=True, + help="Remaining amount due.") + payment_ids = fields.Many2many('account.move.line', string='Payments', + compute='_compute_payments') + move_name = fields.Char(string='Journal Entry', readonly=True, + states={'draft': [('readonly', False)]}, copy=False) + user_id = fields.Many2one('res.users', string='Salesperson', track_visibility='onchange', + readonly=True, states={'draft': [('readonly', False)]}, + default=lambda self: self.env.user) + fiscal_position = fields.Many2one('account.fiscal.position', string='Fiscal Position', + readonly=True, states={'draft': [('readonly', False)]}) + commercial_partner_id = fields.Many2one('res.partner', string='Commercial Entity', + related='partner_id.commercial_partner_id', store=True, readonly=True, + help="The commercial entity that will be used on Journal Entries for this invoice") - 'move_id': fields.many2one('account.move', 'Journal Entry', readonly=True, select=1, ondelete='restrict', help="Link to the automatically generated Journal Items."), - 'amount_untaxed': fields.function(_amount_all, digits_compute=dp.get_precision('Account'), string='Subtotal', track_visibility='always', - store={ - 'account.invoice': (lambda self, cr, uid, ids, c={}: ids, ['invoice_line'], 20), - 'account.invoice.tax': (_get_invoice_tax, None, 20), - 'account.invoice.line': (_get_invoice_line, ['price_unit','invoice_line_tax_id','quantity','discount','invoice_id'], 20), - }, - multi='all'), - 'amount_tax': fields.function(_amount_all, digits_compute=dp.get_precision('Account'), string='Tax', - store={ - 'account.invoice': (lambda self, cr, uid, ids, c={}: ids, ['invoice_line'], 20), - 'account.invoice.tax': (_get_invoice_tax, None, 20), - 'account.invoice.line': (_get_invoice_line, ['price_unit','invoice_line_tax_id','quantity','discount','invoice_id'], 20), - }, - multi='all'), - 'amount_total': fields.function(_amount_all, digits_compute=dp.get_precision('Account'), string='Total', - store={ - 'account.invoice': (lambda self, cr, uid, ids, c={}: ids, ['invoice_line'], 20), - 'account.invoice.tax': (_get_invoice_tax, None, 20), - 'account.invoice.line': (_get_invoice_line, ['price_unit','invoice_line_tax_id','quantity','discount','invoice_id'], 20), - }, - multi='all'), - 'currency_id': fields.many2one('res.currency', 'Currency', required=True, readonly=True, states={'draft':[('readonly',False)]}, track_visibility='always'), - 'journal_id': fields.many2one('account.journal', 'Journal', required=True, readonly=True, states={'draft':[('readonly',False)]}, - domain="[('type', 'in', {'out_invoice': ['sale'], 'out_refund': ['sale_refund'], 'in_refund': ['purchase_refund'], 'in_invoice': ['purchase']}.get(type, [])), ('company_id', '=', company_id)]"), - 'company_id': fields.many2one('res.company', 'Company', required=True, change_default=True, readonly=True, states={'draft':[('readonly',False)]}), - 'check_total': fields.float('Verification Total', digits_compute=dp.get_precision('Account'), readonly=True, states={'draft':[('readonly',False)]}), - 'reconciled': fields.function(_reconciled, string='Paid/Reconciled', type='boolean', - store={ - 'account.invoice': (lambda self, cr, uid, ids, c={}: ids, None, 50), # Check if we can remove ? - 'account.move.line': (_get_invoice_from_line, None, 50), - 'account.move.reconcile': (_get_invoice_from_reconcile, None, 50), - }, help="It indicates that the invoice has been paid and the journal entry of the invoice has been reconciled with one or several journal entries of payment."), - 'partner_bank_id': fields.many2one('res.partner.bank', 'Bank Account', - help='Bank Account Number to which the invoice will be paid. A Company bank account if this is a Customer Invoice or Supplier Refund, otherwise a Partner bank account number.', readonly=True, states={'draft':[('readonly',False)]}), - 'move_lines':fields.function(_get_lines, type='many2many', relation='account.move.line', string='Entry Lines'), - 'residual': fields.function(_amount_residual, digits_compute=dp.get_precision('Account'), string='Balance', - store={ - 'account.invoice': (lambda self, cr, uid, ids, c={}: ids, ['invoice_line','move_id'], 50), - 'account.invoice.tax': (_get_invoice_tax, None, 50), - 'account.invoice.line': (_get_invoice_line, ['price_unit','invoice_line_tax_id','quantity','discount','invoice_id'], 50), - 'account.move.line': (_get_invoice_from_line, None, 50), - 'account.move.reconcile': (_get_invoice_from_reconcile, None, 50), - }, - help="Remaining amount due."), - 'payment_ids': fields.function(_compute_lines, relation='account.move.line', type="many2many", string='Payments'), - 'move_name': fields.char('Journal Entry', readonly=True, states={'draft':[('readonly',False)]}), - 'user_id': fields.many2one('res.users', 'Salesperson', readonly=True, track_visibility='onchange', states={'draft':[('readonly',False)]}), - 'fiscal_position': fields.many2one('account.fiscal.position', 'Fiscal Position', readonly=True, states={'draft':[('readonly',False)]}), - 'commercial_partner_id': fields.related('partner_id', 'commercial_partner_id', string='Commercial Entity', type='many2one', - relation='res.partner', store=True, readonly=True, - help="The commercial entity that will be used on Journal Entries for this invoice") - } - _defaults = { - 'type': _get_type, - 'state': 'draft', - 'journal_id': _get_journal, - 'currency_id': _get_currency, - 'company_id': lambda self,cr,uid,c: self.pool.get('res.company')._company_default_get(cr, uid, 'account.invoice', context=c), - 'reference_type': 'none', - 'check_total': 0.0, - 'internal_number': False, - 'user_id': lambda s, cr, u, c: u, - 'sent': False, - } _sql_constraints = [ - ('number_uniq', 'unique(number, company_id, journal_id, type)', 'Invoice Number must be unique per Company!'), + ('number_uniq', 'unique(number, company_id, journal_id, type)', + 'Invoice Number must be unique per Company!'), ] - - - - def fields_view_get(self, cr, uid, view_id=None, view_type=False, context=None, toolbar=False, submenu=False): - journal_obj = self.pool.get('account.journal') - if context is None: - context = {} - - if context.get('active_model', '') in ['res.partner'] and context.get('active_ids', False) and context['active_ids']: - partner = self.pool[context['active_model']].read(cr, uid, context['active_ids'], ['supplier','customer'])[0] + @api.model + def fields_view_get(self, view_id=None, view_type=False, toolbar=False, submenu=False): + context = self._context + if context.get('active_model') == 'res.partner' and context.get('active_ids'): + partner = self.env['res.partner'].browse(context['active_ids'])[0] if not view_type: - view_id = self.pool.get('ir.ui.view').search(cr, uid, [('name', '=', 'account.invoice.tree')]) + view_id = self.env['ir.ui.view'].search([('name', '=', 'account.invoice.tree')]).id view_type = 'tree' - if view_type == 'form': - if partner['supplier'] and not partner['customer']: - view_id = self.pool.get('ir.ui.view').search(cr,uid,[('name', '=', 'account.invoice.supplier.form')]) - elif partner['customer'] and not partner['supplier']: - view_id = self.pool.get('ir.ui.view').search(cr,uid,[('name', '=', 'account.invoice.form')]) - if view_id and isinstance(view_id, (list, tuple)): - view_id = view_id[0] - res = super(account_invoice,self).fields_view_get(cr, uid, view_id=view_id, view_type=view_type, context=context, toolbar=toolbar, submenu=submenu) + elif view_type == 'form': + if partner.supplier and not partner.customer: + view_id = self.env['ir.ui.view'].search([('name', '=', 'account.invoice.supplier.form')]).id + elif partner.customer and not partner.supplier: + view_id = self.env['ir.ui.view'].search([('name', '=', 'account.invoice.form')]).id - type = context.get('journal_type', False) + res = super(account_invoice, self).fields_view_get(view_id=view_id, view_type=view_type, toolbar=toolbar, submenu=submenu) + + # adapt selection of field journal_id for field in res['fields']: if field == 'journal_id' and type: - journal_select = journal_obj._name_search(cr, uid, '', [('type', '=', type)], context=context, limit=None, name_get_uid=1) + journal_select = self.env['account.journal']._name_search('', [('type', '=', type)], name_get_uid=1) res['fields'][field]['selection'] = journal_select doc = etree.XML(res['arch']) - if context.get('type', False): + if context.get('type'): for node in doc.xpath("//field[@name='partner_bank_id']"): if context['type'] == 'in_refund': node.set('domain', "[('partner_id.ref_companies', 'in', [company_id])]") elif context['type'] == 'out_refund': node.set('domain', "[('partner_id', '=', partner_id)]") - res['arch'] = etree.tostring(doc) if view_type == 'search': - if context.get('type', 'in_invoice') in ('out_invoice', 'out_refund'): + if context.get('type') in ('out_invoice', 'out_refund'): for node in doc.xpath("//group[@name='extended filter']"): doc.remove(node) - res['arch'] = etree.tostring(doc) if view_type == 'tree': partner_string = _('Customer') - if context.get('type', 'out_invoice') in ('in_invoice', 'in_refund'): + if context.get('type') in ('in_invoice', 'in_refund'): partner_string = _('Supplier') for node in doc.xpath("//field[@name='reference']"): node.set('invisible', '0') for node in doc.xpath("//field[@name='partner_id']"): node.set('string', partner_string) - res['arch'] = etree.tostring(doc) + + res['arch'] = etree.tostring(doc) return res - def get_log_context(self, cr, uid, context=None): - if context is None: - context = {} - res = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'account', 'invoice_form') - view_id = res and res[1] or False - context['view_id'] = view_id - return context + @api.multi + def invoice_print(self): + """ Print the invoice and mark it as sent, so that we can see more + easily the next step of the workflow + """ + assert len(self) == 1, 'This option should only be used for a single id at a time.' + self.sent = True + return self.env['report'].get_action(self, 'account.report_invoice') - def invoice_print(self, cr, uid, ids, context=None): - ''' - This function prints the invoice and mark it as sent, so that we can see more easily the next step of the workflow - ''' - assert len(ids) == 1, 'This option should only be used for a single id at a time.' - self.write(cr, uid, ids, {'sent': True}, context=context) - return self.pool['report'].get_action(cr, uid, ids, 'account.report_invoice', context=context) - - def action_invoice_sent(self, cr, uid, ids, context=None): - ''' - This function opens a window to compose an email, with the edi invoice template message loaded by default - ''' - assert len(ids) == 1, 'This option should only be used for a single id at a time.' - ir_model_data = self.pool.get('ir.model.data') - try: - template_id = ir_model_data.get_object_reference(cr, uid, 'account', 'email_template_edi_invoice')[1] - except ValueError: - template_id = False - try: - compose_form_id = ir_model_data.get_object_reference(cr, uid, 'mail', 'email_compose_message_wizard_form')[1] - except ValueError: - compose_form_id = False - ctx = dict(context) - ctx.update({ - 'default_model': 'account.invoice', - 'default_res_id': ids[0], - 'default_use_template': bool(template_id), - 'default_template_id': template_id, - 'default_composition_mode': 'comment', - 'mark_invoice_as_sent': True, - }) + @api.multi + def action_invoice_sent(self): + """ Open a window to compose an email, with the edi invoice template + message loaded by default + """ + assert len(self) == 1, 'This option should only be used for a single id at a time.' + template = self.env.ref('account.email_template_edi_invoice', False) + compose_form = self.env.ref('mail.email_compose_message_wizard_form', False) + ctx = dict(self._context, + default_model='account.invoice', + default_res_id=self.id, + default_use_template=bool(template), + default_template_id=template.id, + default_composition_mode='comment', + mark_invoice_as_sent=True, + ) return { 'name': _('Compose Email'), 'type': 'ir.actions.act_window', 'view_type': 'form', 'view_mode': 'form', 'res_model': 'mail.compose.message', - 'views': [(compose_form_id, 'form')], - 'view_id': compose_form_id, + 'views': [(compose_form.id, 'form')], + 'view_id': compose_form.id, 'target': 'new', 'context': ctx, } - def confirm_paid(self, cr, uid, ids, context=None): - if context is None: - context = {} - self.write(cr, uid, ids, {'state':'paid'}, context=context) - return True + @api.multi + def confirm_paid(self): + return self.write({'state': 'paid'}) - def unlink(self, cr, uid, ids, context=None): - if context is None: - context = {} - invoices = self.read(cr, uid, ids, ['state','internal_number'], context=context) - unlink_ids = [] + @api.multi + def unlink(self): + for invoice in self: + if invoice.state not in ('draft', 'cancel'): + raise Warning(_('You cannot delete an invoice which is not draft or cancelled. You should refund it instead.')) + elif invoice.internal_number: + raise Warning(_('You cannot delete an invoice after it has been validated (and received a number). You can set it back to "Draft" state and modify its content, then re-confirm it.')) + return super(account_invoice, self).unlink() - for t in invoices: - if t['state'] not in ('draft', 'cancel'): - raise openerp.exceptions.Warning(_('You cannot delete an invoice which is not draft or cancelled. You should refund it instead.')) - elif t['internal_number']: - raise openerp.exceptions.Warning(_('You cannot delete an invoice after it has been validated (and received a number). You can set it back to "Draft" state and modify its content, then re-confirm it.')) - else: - unlink_ids.append(t['id']) - - osv.osv.unlink(self, cr, uid, unlink_ids, context=context) - return True - - def onchange_partner_id(self, cr, uid, ids, type, partner_id, - date_invoice=False, payment_term=False, - partner_bank_id=False, company_id=False, - context=None): - partner_payment_term = False - acc_id = False - bank_id = False + @api.multi + def onchange_partner_id(self, type, partner_id, date_invoice=False, + payment_term=False, partner_bank_id=False, company_id=False): + account_id = False + payment_term_id = False fiscal_position = False + bank_id = False - opt = [('uid', str(uid))] if partner_id: - - opt.insert(0, ('id', partner_id)) - p = self.pool.get('res.partner').browse(cr, uid, partner_id, - context=context) + p = self.env['res.partner'].browse(partner_id) + rec_account = p.property_account_receivable + pay_account = p.property_account_payable if company_id: - if (p.property_account_receivable.company_id and (p.property_account_receivable.company_id.id != company_id)) and (p.property_account_payable.company_id and (p.property_account_payable.company_id.id != company_id)): - property_obj = self.pool.get('ir.property') - rec_pro_id = property_obj.search(cr,uid,[('name','=','property_account_receivable'),('res_id','=','res.partner,'+str(partner_id)+''),('company_id','=',company_id)]) - pay_pro_id = property_obj.search(cr,uid,[('name','=','property_account_payable'),('res_id','=','res.partner,'+str(partner_id)+''),('company_id','=',company_id)]) - if not rec_pro_id: - rec_pro_id = property_obj.search(cr,uid,[('name','=','property_account_receivable'),('company_id','=',company_id)]) - if not pay_pro_id: - pay_pro_id = property_obj.search(cr,uid,[('name','=','property_account_payable'),('company_id','=',company_id)]) - rec_line_data = property_obj.read(cr,uid,rec_pro_id,['name','value_reference','res_id']) - pay_line_data = property_obj.read(cr,uid,pay_pro_id,['name','value_reference','res_id']) - rec_res_id = rec_line_data and rec_line_data[0].get('value_reference',False) and int(rec_line_data[0]['value_reference'].split(',')[1]) or False - pay_res_id = pay_line_data and pay_line_data[0].get('value_reference',False) and int(pay_line_data[0]['value_reference'].split(',')[1]) or False - if not rec_res_id and not pay_res_id: - model, action_id = self.pool['ir.model.data'].get_object_reference(cr, uid, 'account', 'action_account_config') + if p.property_account_receivable.company_id and \ + p.property_account_receivable.company_id.id != company_id and \ + p.property_account_payable.company_id and \ + p.property_account_payable.company_id.id != company_id: + prop = self.env['ir.property'] + rec_dom = [('name', '=', 'property_account_receivable'), ('company_id', '=', company_id)] + pay_dom = [('name', '=', 'property_account_payable'), ('company_id', '=', company_id)] + res_dom = [('res_id', '=', 'res.partner,%s' % partner_id)] + rec_prop = prop.search(rec_dom + res_dom) or prop.search(rec_dom) + pay_prop = prop.search(pay_dom + res_dom) or prop.search(pay_dom) + rec_account = rec_prop.get_by_record(rec_prop) + pay_account = pay_prop.get_by_record(pay_prop) + if not rec_account and not pay_account: + action = self.env.ref('account.action_account_config') msg = _('Cannot find a chart of accounts for this company, You should configure it. \nPlease go to Account Configuration.') - raise openerp.exceptions.RedirectWarning(msg, action_id, _('Go to the configuration panel')) - account_obj = self.pool.get('account.account') - rec_obj_acc = account_obj.browse(cr, uid, [rec_res_id], - context=context) - pay_obj_acc = account_obj.browse(cr, uid, [pay_res_id], - context=context) - p.property_account_receivable = rec_obj_acc[0] - p.property_account_payable = pay_obj_acc[0] + raise RedirectWarning(msg, action.id, _('Go to the configuration panel')) if type in ('out_invoice', 'out_refund'): - acc_id = p.property_account_receivable.id - partner_payment_term = p.property_payment_term and p.property_payment_term.id or False + account_id = rec_account.id + payment_term_id = p.property_payment_term.id else: - acc_id = p.property_account_payable.id - partner_payment_term = p.property_supplier_payment_term and p.property_supplier_payment_term.id or False - fiscal_position = p.property_account_position and p.property_account_position.id or False - if p.bank_ids: - bank_id = p.bank_ids[0].id + account_id = pay_account.id + payment_term_id = p.property_supplier_payment_term.id + fiscal_position = p.property_account_position.id + bank_id = p.bank_ids.id result = {'value': { - 'account_id': acc_id, - 'payment_term': partner_payment_term, - 'fiscal_position': fiscal_position - } - } + 'account_id': account_id, + 'payment_term': payment_term_id, + 'fiscal_position': fiscal_position, + }} if type in ('in_invoice', 'in_refund'): result['value']['partner_bank_id'] = bank_id - if payment_term != partner_payment_term: - if partner_payment_term: - to_update = self.onchange_payment_term_date_invoice( - cr, uid, ids, partner_payment_term, date_invoice) - result['value'].update(to_update['value']) + if payment_term != payment_term_id: + if payment_term_id: + to_update = self.onchange_payment_term_date_invoice(payment_term_id, date_invoice) + result['value'].update(to_update.get('value', {})) else: result['value']['date_due'] = False if partner_bank_id != bank_id: - to_update = self.onchange_partner_bank(cr, uid, ids, bank_id) - result['value'].update(to_update['value']) + to_update = self.onchange_partner_bank(bank_id) + result['value'].update(to_update.get('value', {})) + return result - def onchange_journal_id(self, cr, uid, ids, journal_id=False, context=None): - result = {} + @api.multi + def onchange_journal_id(self, journal_id=False): if journal_id: - journal = self.pool.get('account.journal').browse(cr, uid, journal_id, context=context) - currency_id = journal.currency and journal.currency.id or journal.company_id.currency_id.id - company_id = journal.company_id.id - result = {'value': { - 'currency_id': currency_id, - 'company_id': company_id, - } + journal = self.env['account.journal'].browse(journal_id) + return { + 'value': { + 'currency_id': journal.currency.id or journal.company_id.currency_id.id, + 'company_id': journal.company_id.id, } - return result - - def onchange_payment_term_date_invoice(self, cr, uid, ids, payment_term_id, date_invoice): - res = {} - if isinstance(ids, (int, long)): - ids = [ids] - if not date_invoice: - date_invoice = time.strftime('%Y-%m-%d') - if not payment_term_id: - inv = self.browse(cr, uid, ids[0]) - #To make sure the invoice due date should contain due date which is entered by user when there is no payment term defined - return {'value':{'date_due': inv.date_due and inv.date_due or date_invoice}} - pterm_list = self.pool.get('account.payment.term').compute(cr, uid, payment_term_id, value=1, date_ref=date_invoice) - if pterm_list: - pterm_list = [line[0] for line in pterm_list] - pterm_list.sort() - res = {'value':{'date_due': pterm_list[-1]}} - else: - raise osv.except_osv(_('Insufficient Data!'), _('The payment term of supplier does not have a payment term line.')) - return res - - def onchange_invoice_line(self, cr, uid, ids, lines): + } return {} - def onchange_partner_bank(self, cursor, user, ids, partner_bank_id=False): + @api.multi + def onchange_payment_term_date_invoice(self, payment_term_id, date_invoice): + if not date_invoice: + date_invoice = fields.Date.today() + if not payment_term_id: + # To make sure the invoice due date should contain due date which is + # entered by user when there is no payment term defined + return {'value': {'date_due': self.date_due or date_invoice}} + pterm = self.env['account.payment.term'].browse(payment_term_id) + pterm_list = pterm.compute(value=1, date_ref=date_invoice)[0] + if pterm_list: + return {'value': {'date_due': max(line[0] for line in pterm_list)}} + else: + raise except_orm(_('Insufficient Data!'), + _('The payment term of supplier does not have a payment term line.')) + + @api.multi + def onchange_invoice_line(self, lines): + return {} + + @api.multi + def onchange_partner_bank(self, partner_bank_id=False): return {'value': {}} - def onchange_company_id(self, cr, uid, ids, company_id, part_id, type, invoice_line, currency_id, context=None): - #TODO: add the missing context parameter when forward-porting in trunk so we can remove - # this hack! - context = self.pool['res.users'].context_get(cr, uid) + @api.multi + def onchange_company_id(self, company_id, part_id, type, invoice_line, currency_id): + # TODO: add the missing context parameter when forward-porting in trunk + # so we can remove this hack! + self = self.with_context(self.env['res.users'].context_get()) - val = {} - dom = {} - obj_journal = self.pool.get('account.journal') - account_obj = self.pool.get('account.account') - inv_line_obj = self.pool.get('account.invoice.line') + values = {} + domain = {} if company_id and part_id and type: - acc_id = False - partner_obj = self.pool.get('res.partner').browse(cr, uid, part_id, context=context) + p = self.env['res.partner'].browse(part_id) + if p.property_account_payable and p.property_account_receivable and \ + p.property_account_payable.company_id.id != company_id and \ + p.property_account_receivable.company_id.id != company_id: + prop = self.env['ir.property'] + rec_dom = [('name', '=', 'property_account_receivable'), ('company_id', '=', company_id)] + pay_dom = [('name', '=', 'property_account_payable'), ('company_id', '=', company_id)] + res_dom = [('res_id', '=', 'res.partner,%s' % part_id)] + rec_prop = prop.search(rec_dom + res_dom) or prop.search(rec_dom) + pay_prop = prop.search(pay_dom + res_dom) or prop.search(pay_dom) + rec_account = rec_prop.get_by_record(rec_prop) + pay_account = pay_prop.get_by_record(pay_prop) + if not rec_account and not pay_account: + action = self.env.ref('account.action_account_config') + msg = _('Cannot find a chart of accounts for this company, You should configure it. \nPlease go to Account Configuration.') + raise RedirectWarning(msg, action.id, _('Go to the configuration panel')) - if partner_obj.property_account_payable and partner_obj.property_account_receivable: - if partner_obj.property_account_payable.company_id.id != company_id and partner_obj.property_account_receivable.company_id.id != company_id: - property_obj = self.pool.get('ir.property') - rec_pro_id = property_obj.search(cr, uid, [('name','=','property_account_receivable'),('res_id','=','res.partner,'+str(part_id)+''),('company_id','=',company_id)]) - pay_pro_id = property_obj.search(cr, uid, [('name','=','property_account_payable'),('res_id','=','res.partner,'+str(part_id)+''),('company_id','=',company_id)]) + if type in ('out_invoice', 'out_refund'): + acc_id = rec_account.id + else: + acc_id = pay_account.id + values= {'account_id': acc_id} - if not rec_pro_id: - rec_pro_id = property_obj.search(cr, uid, [('name','=','property_account_receivable'),('company_id','=',company_id)]) - if not pay_pro_id: - pay_pro_id = property_obj.search(cr, uid, [('name','=','property_account_payable'),('company_id','=',company_id)]) - - rec_line_data = property_obj.read(cr, uid, rec_pro_id, ['name','value_reference','res_id']) - pay_line_data = property_obj.read(cr, uid, pay_pro_id, ['name','value_reference','res_id']) - rec_res_id = rec_line_data and rec_line_data[0].get('value_reference',False) and int(rec_line_data[0]['value_reference'].split(',')[1]) or False - pay_res_id = pay_line_data and pay_line_data[0].get('value_reference',False) and int(pay_line_data[0]['value_reference'].split(',')[1]) or False - - if not rec_res_id and not pay_res_id: - model, action_id = self.pool['ir.model.data'].get_object_reference(cr, uid, 'account', 'action_account_config') - msg = _('Cannot find a chart of accounts for this company, You should configure it. \nPlease go to Account Configuration.') - raise openerp.exceptions.RedirectWarning(msg, action_id, _('Go to the configuration panel')) - if type in ('out_invoice', 'out_refund'): - acc_id = rec_res_id - else: - acc_id = pay_res_id - - val= {'account_id': acc_id} - if ids: + if self: if company_id: - inv_obj = self.browse(cr,uid,ids) - for line in inv_obj[0].invoice_line: - if line.account_id: - if line.account_id.company_id.id != company_id: - result_id = account_obj.search(cr, uid, [('name','=',line.account_id.name),('company_id','=',company_id)]) - if not result_id: - model, action_id = self.pool['ir.model.data'].get_object_reference(cr, uid, 'account', 'action_account_config') - msg = _('Cannot find a chart of accounts for this company, You should configure it. \nPlease go to Account Configuration.') - raise openerp.exceptions.RedirectWarning(msg, action_id, _('Go to the configuration panel')) - inv_line_obj.write(cr, uid, [line.id], {'account_id': result_id[-1]}) - else: - if invoice_line: - for inv_line in invoice_line: - obj_l = account_obj.browse(cr, uid, inv_line[2]['account_id']) - if obj_l.company_id.id != company_id: - raise osv.except_osv(_('Configuration Error!'), - _('Invoice line account\'s company and invoice\'s company does not match.')) - else: + for line in self.invoice_line: + if not line.account_id: continue + if line.account_id.company_id.id == company_id: + continue + accounts = self.env['account.account'].search([('name', '=', line.account_id.name), ('company_id', '=', company_id)]) + if not accounts: + action = self.env.ref('account.action_account_config') + msg = _('Cannot find a chart of accounts for this company, You should configure it. \nPlease go to Account Configuration.') + raise RedirectWarning(msg, action.id, _('Go to the configuration panel')) + line.write({'account_id': accounts[-1].id}) + else: + for line_cmd in invoice_line or []: + if len(line_cmd) >= 3 and isinstance(line_cmd[2], dict): + line = self.env['account.account'].browse(line_cmd[2]['account_id']) + if line.company_id.id != company_id: + raise except_orm( + _('Configuration Error!'), + _("Invoice line account's company and invoice's company does not match.") + ) + if company_id and type: - journal_mapping = { - 'out_invoice': 'sale', - 'out_refund': 'sale_refund', - 'in_refund': 'purchase_refund', - 'in_invoice': 'purchase', - } - journal_type = journal_mapping[type] - journal_ids = obj_journal.search(cr, uid, [('company_id','=',company_id), ('type', '=', journal_type)]) - if journal_ids: - val['journal_id'] = journal_ids[0] - ir_values_obj = self.pool.get('ir.values') - res_journal_default = ir_values_obj.get(cr, uid, 'default', 'type=%s' % (type), ['account.invoice']) - for r in res_journal_default: - if r[1] == 'journal_id' and r[2] in journal_ids: - val['journal_id'] = r[2] - if not val.get('journal_id', False): - journal_type_map = dict(obj_journal._columns['type'].selection) - journal_type_label = self.pool['ir.translation']._get_source(cr, uid, None, ('code','selection'), - context.get('lang'), - journal_type_map.get(journal_type)) - model, action_id = self.pool['ir.model.data'].get_object_reference(cr, uid, 'account', 'action_account_journal_form') - msg = _("""Cannot find any account journal of type "%s" for this company, You should create one.\n Please go to Journal Configuration""") % journal_type_label - raise openerp.exceptions.RedirectWarning(msg, action_id, _('Go to the configuration panel')) - dom = {'journal_id': [('id', 'in', journal_ids)]} - else: - journal_ids = obj_journal.search(cr, uid, []) + journal_type = TYPE2JOURNAL[type] + journals = self.env['account.journal'].search([('type', '=', journal_type), ('company_id', '=', company_id)]) + if journals: + values['journal_id'] = journals[0].id + journal_defaults = self.env['ir.values'].get_defaults_dict('account.invoice', 'type=%s' % type) + if 'journal_id' in journal_defaults: + values['journal_id'] = journal_defaults['journal_id'] + if not values.get('journal_id'): + field_desc = journals.fields_get(['journal_id']) + type_label = next(t for t, label in field_desc['journal_id']['selection'] if t == journal_type) + action = self.env.ref('account.action_account_journal_form') + msg = _('Cannot find any account journal of type "%s" for this company, You should create one.\n Please go to Journal Configuration') % type_label + raise RedirectWarning(msg, action.id, _('Go to the configuration panel')) + domain = {'journal_id': [('id', 'in', journals.ids)]} - return {'value': val, 'domain': dom} + return {'value': values, 'domain': domain} - # go from canceled state to draft state - def action_cancel_draft(self, cr, uid, ids, *args): - self.write(cr, uid, ids, {'state':'draft'}) - self.delete_workflow(cr, uid, ids) - self.create_workflow(cr, uid, ids) + @api.multi + def action_cancel_draft(self): + # go from canceled state to draft state + self.write({'state': 'draft'}) + self.delete_workflow() + self.create_workflow() return True - def get_formview_id(self, cr, uid, id, context=None): + @api.one + @api.returns('ir.ui.view') + def get_formview_id(self): """ Update form view id of action to open the invoice """ - obj = self.browse(cr, uid, id, context=context) - if obj.type == 'in_invoice': - model, view_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'account', 'invoice_supplier_form') + if self.type == 'in_invoice': + return self.env.ref('account.invoice_supplier_form') else: - model, view_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'account', 'invoice_form') - return view_id + return self.env.ref('account.invoice_form') - # Workflow stuff - ################# + @api.multi + def move_line_id_payment_get(self): + # return the move line ids with the same account as the invoice self + if not self.id: + return [] + query = """ SELECT l.id + FROM account_move_line l, account_invoice i + WHERE i.id = %s AND l.move_id = i.move_id AND l.account_id = i.account_id + """ + self._cr.execute(query, (self.id,)) + return [row[0] for row in self._cr.fetchall()] - # return the ids of the move lines which has the same account than the invoice - # whose id is in ids - def move_line_id_payment_get(self, cr, uid, ids, *args): - if not ids: return [] - result = self.move_line_id_payment_gets(cr, uid, ids, *args) - return result.get(ids[0], []) - - def move_line_id_payment_gets(self, cr, uid, ids, *args): - res = {} - if not ids: return res - cr.execute('SELECT i.id, l.id '\ - 'FROM account_move_line l '\ - 'LEFT JOIN account_invoice i ON (i.move_id=l.move_id) '\ - 'WHERE i.id IN %s '\ - 'AND l.account_id=i.account_id', - (tuple(ids),)) - for r in cr.fetchall(): - res.setdefault(r[0], []) - res[r[0]].append( r[1] ) - return res - - def copy(self, cr, uid, id, default=None, context=None): - default = default or {} - default.update({ - 'state':'draft', - 'number':False, - 'move_id':False, - 'move_name':False, - 'internal_number': False, - 'period_id': False, - 'sent': False, - }) - if 'date_invoice' not in default: - default.update({ - 'date_invoice':False - }) - if 'date_due' not in default: - default.update({ - 'date_due':False - }) - return super(account_invoice, self).copy(cr, uid, id, default, context) - - def test_paid(self, cr, uid, ids, *args): - res = self.move_line_id_payment_get(cr, uid, ids) - if not res: + @api.multi + def test_paid(self): + # check whether all corresponding account move lines are reconciled + line_ids = self.move_line_id_payment_get() + if not line_ids: return False - ok = True - for id in res: - cr.execute('select reconcile_id from account_move_line where id=%s', (id,)) - ok = ok and bool(cr.fetchone()[0]) - return ok + query = "SELECT reconcile_id FROM account_move_line WHERE id IN %s" + self._cr.execute(query, (tuple(line_ids),)) + return all(row[0] for row in self._cr.fetchall()) - def button_reset_taxes(self, cr, uid, ids, context=None): - if context is None: - context = {} - ctx = context.copy() - ait_obj = self.pool.get('account.invoice.tax') - for id in ids: - cr.execute("DELETE FROM account_invoice_tax WHERE invoice_id=%s AND manual is False", (id,)) - partner = self.browse(cr, uid, id, context=ctx).partner_id + @api.multi + def button_reset_taxes(self): + account_invoice_tax = self.env['account.invoice.tax'] + ctx = dict(self._context) + for invoice in self: + self._cr.execute("DELETE FROM account_invoice_tax WHERE invoice_id=%s AND manual is False", (invoice.id,)) + self.invalidate_cache() + partner = invoice.partner_id if partner.lang: - ctx.update({'lang': partner.lang}) - for taxe in ait_obj.compute(cr, uid, id, context=ctx).values(): - ait_obj.create(cr, uid, taxe) - # Update the stored value (fields.function), so we write to trigger recompute - self.pool.get('account.invoice').write(cr, uid, ids, {'invoice_line':[]}, context=ctx) - return True + ctx['lang'] = partner.lang + for taxe in account_invoice_tax.compute(invoice).values(): + account_invoice_tax.create(taxe) + # dummy write on self to trigger recomputations + return self.with_context(ctx).write({'invoice_line': []}) - def button_compute(self, cr, uid, ids, context=None, set_total=False): - self.button_reset_taxes(cr, uid, ids, context) - for inv in self.browse(cr, uid, ids, context=context): + @api.multi + def button_compute(self, set_total=False): + self.button_reset_taxes() + for invoice in self: if set_total: - self.pool.get('account.invoice').write(cr, uid, [inv.id], {'check_total': inv.amount_total}) + invoice.check_total = invoice.amount_total return True - def _convert_ref(self, cr, uid, ref): + @staticmethod + def _convert_ref(ref): return (ref or '').replace('/','') - def _get_analytic_lines(self, cr, uid, id, context=None): - if context is None: - context = {} - inv = self.browse(cr, uid, id) - cur_obj = self.pool.get('res.currency') + @api.multi + def _get_analytic_lines(self): + """ Return a list of dict for creating analytic lines for self[0] """ + company_currency = self.company_id.currency_id + sign = 1 if self.type in ('out_invoice', 'in_refund') else -1 - company_currency = self.pool['res.company'].browse(cr, uid, inv.company_id.id).currency_id.id - if inv.type in ('out_invoice', 'in_refund'): - sign = 1 - else: - sign = -1 - - iml = self.pool.get('account.invoice.line').move_line_get(cr, uid, inv.id, context=context) + iml = self.env['account.invoice.line'].move_line_get(self.id) for il in iml: if il['account_analytic_id']: - if inv.type in ('in_invoice', 'in_refund'): - ref = inv.reference + if self.type in ('in_invoice', 'in_refund'): + ref = self.reference else: - ref = self._convert_ref(cr, uid, inv.number) - if not inv.journal_id.analytic_journal_id: - raise osv.except_osv(_('No Analytic Journal!'),_("You have to define an analytic journal on the '%s' journal!") % (inv.journal_id.name,)) + ref = self._convert_ref(self.number) + if not self.journal_id.analytic_journal_id: + raise except_orm(_('No Analytic Journal!'), + _("You have to define an analytic journal on the '%s' journal!") % (self.journal_id.name,)) + currency = self.currency_id.with_context(date=self.date_invoice) il['analytic_lines'] = [(0,0, { 'name': il['name'], - 'date': inv['date_invoice'], + 'date': self.date_invoice, 'account_id': il['account_analytic_id'], 'unit_amount': il['quantity'], - 'amount': cur_obj.compute(cr, uid, inv.currency_id.id, company_currency, il['price'], context={'date': inv.date_invoice}) * sign, + 'amount': currency.compute(il['price'], company_currency) * sign, 'product_id': il['product_id'], 'product_uom_id': il['uos_id'], 'general_account_id': il['account_id'], - 'journal_id': inv.journal_id.analytic_journal_id.id, + 'journal_id': self.journal_id.analytic_journal_id.id, 'ref': ref, })] return iml - def action_date_assign(self, cr, uid, ids, *args): - for inv in self.browse(cr, uid, ids): - res = self.onchange_payment_term_date_invoice(cr, uid, inv.id, inv.payment_term.id, inv.date_invoice) - if res and res['value']: - self.write(cr, uid, [inv.id], res['value']) + @api.multi + def action_date_assign(self): + for inv in self: + res = inv.onchange_payment_term_date_invoice(inv.payment_term.id, inv.date_invoice) + if res and res.get('value'): + inv.write(res['value']) return True - def finalize_invoice_move_lines(self, cr, uid, invoice_browse, move_lines): - """finalize_invoice_move_lines(cr, uid, invoice, move_lines) -> move_lines - Hook method to be overridden in additional modules to verify and possibly alter the - move lines to be created by an invoice, for special cases. - :param invoice_browse: browsable record of the invoice that is generating the move lines - :param move_lines: list of dictionaries with the account.move.lines (as for create()) - :return: the (possibly updated) final move_lines to create for this invoice + @api.multi + def finalize_invoice_move_lines(self, move_lines): + """ finalize_invoice_move_lines(move_lines) -> move_lines + + Hook method to be overridden in additional modules to verify and + possibly alter the move lines to be created by an invoice, for + special cases. + :param move_lines: list of dictionaries with the account.move.lines (as for create()) + :return: the (possibly updated) final move_lines to create for this invoice """ return move_lines - def check_tax_lines(self, cr, uid, inv, compute_taxes, ait_obj): - company_currency = self.pool['res.company'].browse(cr, uid, inv.company_id.id).currency_id - if not inv.tax_line: + @api.multi + def check_tax_lines(self, compute_taxes): + account_invoice_tax = self.env['account.invoice.tax'] + company_currency = self.company_id.currency_id + if not self.tax_line: for tax in compute_taxes.values(): - ait_obj.create(cr, uid, tax) + account_invoice_tax.create(tax) else: tax_key = [] - for tax in inv.tax_line: + for tax in self.tax_line: if tax.manual: continue key = (tax.tax_code_id.id, tax.base_code_id.id, tax.account_id.id, tax.account_analytic_id.id) tax_key.append(key) - if not key in compute_taxes: - raise osv.except_osv(_('Warning!'), _('Global taxes defined, but they are not in invoice lines !')) + if key not in compute_taxes: + raise except_orm(_('Warning!'), _('Global taxes defined, but they are not in invoice lines !')) base = compute_taxes[key]['base'] if abs(base - tax.base) > company_currency.rounding: - raise osv.except_osv(_('Warning!'), _('Tax base different!\nClick on compute to update the tax base.')) + raise except_orm(_('Warning!'), _('Tax base different!\nClick on compute to update the tax base.')) for key in compute_taxes: - if not key in tax_key: - raise osv.except_osv(_('Warning!'), _('Taxes are missing!\nClick on compute button.')) + if key not in tax_key: + raise except_orm(_('Warning!'), _('Taxes are missing!\nClick on compute button.')) - def compute_invoice_totals(self, cr, uid, inv, company_currency, ref, invoice_move_lines, context=None): - if context is None: - context={} + @api.multi + def compute_invoice_totals(self, company_currency, ref, invoice_move_lines): total = 0 total_currency = 0 - cur_obj = self.pool.get('res.currency') - for i in invoice_move_lines: - if inv.currency_id.id != company_currency: - context.update({'date': inv.date_invoice or time.strftime('%Y-%m-%d')}) - i['currency_id'] = inv.currency_id.id - i['amount_currency'] = i['price'] - i['price'] = cur_obj.compute(cr, uid, inv.currency_id.id, - company_currency, i['price'], - context=context) + for line in invoice_move_lines: + if self.currency_id != company_currency: + currency = self.currency_id.with_context(date=self.date_invoice or fields.Date.today()) + line['currency_id'] = currency.id + line['amount_currency'] = line['price'] + line['price'] = currency.compute(line['price'], company_currency) else: - i['amount_currency'] = False - i['currency_id'] = False - i['ref'] = ref - if inv.type in ('out_invoice','in_refund'): - total += i['price'] - total_currency += i['amount_currency'] or i['price'] - i['price'] = - i['price'] + line['currency_id'] = False + line['amount_currency'] = False + line['ref'] = ref + if self.type in ('out_invoice','in_refund'): + total += line['price'] + total_currency += line['amount_currency'] or line['price'] + line['price'] = - line['price'] else: - total -= i['price'] - total_currency -= i['amount_currency'] or i['price'] + total -= line['price'] + total_currency -= line['amount_currency'] or line['price'] return total, total_currency, invoice_move_lines - def inv_line_characteristic_hashcode(self, invoice, invoice_line): + def inv_line_characteristic_hashcode(self, invoice_line): """Overridable hashcode generation for invoice lines. Lines having the same hashcode will be grouped together if the journal has the 'group line' option. Of course a module can add fields to invoice lines that would need to be tested too before merging lines or not.""" - return "%s-%s-%s-%s-%s"%( + return "%s-%s-%s-%s-%s" % ( invoice_line['account_id'], - invoice_line.get('tax_code_id',"False"), - invoice_line.get('product_id',"False"), - invoice_line.get('analytic_account_id',"False"), - invoice_line.get('date_maturity',"False")) + invoice_line.get('tax_code_id', 'False'), + invoice_line.get('product_id', 'False'), + invoice_line.get('analytic_account_id', 'False'), + invoice_line.get('date_maturity', 'False'), + ) - def group_lines(self, cr, uid, iml, line, inv): + def group_lines(self, iml, line): """Merge account move lines (and hence analytic lines) if invoice line hashcodes are equals""" - if inv.journal_id.group_invoice_lines: + if self.journal_id.group_invoice_lines: line2 = {} for x, y, l in line: - tmp = self.inv_line_characteristic_hashcode(inv, l) - + tmp = self.inv_line_characteristic_hashcode(l) if tmp in line2: am = line2[tmp]['debit'] - line2[tmp]['credit'] + (l['debit'] - l['credit']) line2[tmp]['debit'] = (am > 0) and am or 0.0 @@ -902,43 +778,35 @@ class account_invoice(osv.osv): line.append((0,0,val)) return line - def action_move_create(self, cr, uid, ids, context=None): - """Creates invoice related analytics and financial move lines""" - ait_obj = self.pool.get('account.invoice.tax') - cur_obj = self.pool.get('res.currency') - period_obj = self.pool.get('account.period') - payment_term_obj = self.pool.get('account.payment.term') - journal_obj = self.pool.get('account.journal') - move_obj = self.pool.get('account.move') - if context is None: - context = {} - inv_date = {} - for inv in self.browse(cr, uid, ids, context=context): + @api.multi + def action_move_create(self): + """ Creates invoice related analytics and financial move lines """ + account_invoice_tax = self.env['account.invoice.tax'] + account_move = self.env['account.move'] + + for inv in self: if not inv.journal_id.sequence_id: - raise osv.except_osv(_('Error!'), _('Please define sequence on the journal related to this invoice.')) + raise except_orm(_('Error!'), _('Please define sequence on the journal related to this invoice.')) if not inv.invoice_line: - raise osv.except_osv(_('No Invoice Lines!'), _('Please create some invoice lines.')) + raise except_orm(_('No Invoice Lines!'), _('Please create some invoice lines.')) if inv.move_id: continue - ctx = context.copy() - ctx.update({'lang': inv.partner_id.lang}) - date_invoice = inv.date_invoice or fields.date.context_today(self,cr,uid,context=context) - inv_date = {'date_invoice': date_invoice} - company_currency = self.pool['res.company'].browse(cr, uid, inv.company_id.id).currency_id.id - # create the analytical lines - # one move line per invoice line - iml = self._get_analytic_lines(cr, uid, inv.id, context=ctx) + ctx = dict(self._context, lang=inv.partner_id.lang) + date_invoice = inv.date_invoice or fields.Date.context_today(self) + + company_currency = inv.company_id.currency_id + # create the analytical lines, one move line per invoice line + iml = inv._get_analytic_lines() # check if taxes are all computed - compute_taxes = ait_obj.compute(cr, uid, inv.id, context=ctx) - self.check_tax_lines(cr, uid, inv, compute_taxes, ait_obj) + compute_taxes = account_invoice_tax.compute(inv) + inv.check_tax_lines(compute_taxes) # I disabled the check_total feature - group_check_total_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'account', 'group_supplier_inv_check_total')[1] - group_check_total = self.pool.get('res.groups').browse(cr, uid, group_check_total_id, context=context) - if group_check_total and uid in [x.id for x in group_check_total.users]: - if (inv.type in ('in_invoice', 'in_refund') and abs(inv.check_total - inv.amount_total) >= (inv.currency_id.rounding/2.0)): - raise osv.except_osv(_('Bad Total!'), _('Please verify the price of the invoice!\nThe encoded total does not match the computed total.')) + group_check_total = self.env.ref('account.group_supplier_inv_check_total') + if self.env.user in group_check_total.users: + if inv.type in ('in_invoice', 'in_refund') and abs(inv.check_total - inv.amount_total) >= (inv.currency_id.rounding / 2.0): + raise except_orm(_('Bad Total!'), _('Please verify the price of the invoice!\nThe encoded total does not match the computed total.')) if inv.payment_term: total_fixed = total_percent = 0 @@ -949,54 +817,46 @@ class account_invoice(osv.osv): total_percent += line.value_amount total_fixed = (total_fixed * 100) / (inv.amount_total or 1.0) if (total_fixed + total_percent) > 100: - raise osv.except_osv(_('Error!'), _("Cannot create the invoice.\nThe related payment term is probably misconfigured as it gives a computed amount greater than the total invoiced amount. In order to avoid rounding issues, the latest line of your payment term must be of type 'balance'.")) + raise except_orm(_('Error!'), _("Cannot create the invoice.\nThe related payment term is probably misconfigured as it gives a computed amount greater than the total invoiced amount. In order to avoid rounding issues, the latest line of your payment term must be of type 'balance'.")) # one move line per tax line - iml += ait_obj.move_line_get(cr, uid, inv.id) + iml += account_invoice_tax.move_line_get(inv.id) if inv.type in ('in_invoice', 'in_refund'): ref = inv.reference else: - ref = self._convert_ref(cr, uid, inv.number) + ref = self._convert_ref(inv.number) - diff_currency_p = inv.currency_id.id <> company_currency + diff_currency = inv.currency_id != company_currency # create one move line for the total and possibly adjust the other lines amount - total = 0 - total_currency = 0 - total, total_currency, iml = self.compute_invoice_totals(cr, uid, inv, company_currency, ref, iml, context=ctx) - acc_id = inv.account_id.id + total, total_currency, iml = inv.with_context(ctx).compute_invoice_totals(company_currency, ref, iml) - name = inv['name'] or inv['supplier_invoice_number'] or '/' - totlines = False + name = inv.name or inv.supplier_invoice_number or '/' + totlines = [] if inv.payment_term: - totlines = payment_term_obj.compute(cr, - uid, inv.payment_term.id, total, date_invoice or False, context=ctx) + totlines = inv.with_context(ctx).payment_term.compute(total, date_invoice)[0] if totlines: res_amount_currency = total_currency - i = 0 - ctx.update({'date': date_invoice}) - for t in totlines: - if inv.currency_id.id != company_currency: - amount_currency = cur_obj.compute(cr, uid, company_currency, inv.currency_id.id, t[1], context=ctx) + ctx['date'] = date_invoice + for i, t in enumerate(totlines): + if inv.currency_id != company_currency: + amount_currency = company_currency.with_context(ctx).compute(t[1], inv.currency_id) else: amount_currency = False - # last line add the diff + # last line: add the diff res_amount_currency -= amount_currency or 0 - i += 1 - if i == len(totlines): + if i + 1 == len(totlines): amount_currency += res_amount_currency iml.append({ 'type': 'dest', 'name': name, 'price': t[1], - 'account_id': acc_id, + 'account_id': inv.account_id.id, 'date_maturity': t[0], - 'amount_currency': diff_currency_p \ - and amount_currency or False, - 'currency_id': diff_currency_p \ - and inv.currency_id.id or False, + 'amount_currency': diff_currency and amount_currency, + 'currency_id': diff_currency and inv.currency_id.id, 'ref': ref, }) else: @@ -1004,579 +864,542 @@ class account_invoice(osv.osv): 'type': 'dest', 'name': name, 'price': total, - 'account_id': acc_id, - 'date_maturity': inv.date_due or False, - 'amount_currency': diff_currency_p \ - and total_currency or False, - 'currency_id': diff_currency_p \ - and inv.currency_id.id or False, + 'account_id': inv.account_id.id, + 'date_maturity': inv.date_due, + 'amount_currency': diff_currency and total_currency, + 'currency_id': diff_currency and inv.currency_id.id, 'ref': ref - }) + }) - date = date_invoice or time.strftime('%Y-%m-%d') + date = date_invoice - part = self.pool.get("res.partner")._find_accounting_partner(inv.partner_id) + part = self.env['res.partner']._find_accounting_partner(inv.partner_id) - line = map(lambda x:(0,0,self.line_get_convert(cr, uid, x, part.id, date, context=ctx)),iml) + line = [(0, 0, self.line_get_convert(l, part.id, date)) for l in iml] + line = inv.group_lines(iml, line) - line = self.group_lines(cr, uid, iml, line, inv) - - journal_id = inv.journal_id.id - journal = journal_obj.browse(cr, uid, journal_id, context=ctx) + journal = inv.journal_id.with_context(ctx) if journal.centralisation: - raise osv.except_osv(_('User Error!'), + raise except_orm(_('User Error!'), _('You cannot create an invoice on a centralized journal. Uncheck the centralized counterpart box in the related journal from the configuration menu.')) - line = self.finalize_invoice_move_lines(cr, uid, inv, line) + line = inv.finalize_invoice_move_lines(line) - move = { - 'ref': inv.reference and inv.reference or inv.name, + move_vals = { + 'ref': inv.reference or inv.name, 'line_id': line, - 'journal_id': journal_id, + 'journal_id': journal.id, 'date': date, 'narration': inv.comment, 'company_id': inv.company_id.id, } - period_id = inv.period_id and inv.period_id.id or False - ctx.update(company_id=inv.company_id.id) - if not period_id: - period_ids = period_obj.find(cr, uid, date_invoice, context=ctx) - period_id = period_ids and period_ids[0] or False - if period_id: - move['period_id'] = period_id + ctx['company_id'] = inv.company_id.id + period = inv.period_id + if not period: + period = period.with_context(ctx).find(date_invoice)[:1] + if period: + move_vals['period_id'] = period.id for i in line: - i[2]['period_id'] = period_id + i[2]['period_id'] = period.id - ctx.update(invoice=inv) - move_id = move_obj.create(cr, uid, move, context=ctx) - new_move_name = move_obj.browse(cr, uid, move_id, context=ctx).name + ctx['invoice'] = inv + move = account_move.with_context(ctx).create(move_vals) # make the invoice point to that move - vals = inv_date - vals.update(move_id=move_id, period_id=period_id, move_name=new_move_name) - self.write(cr, uid, [inv.id], vals, context=ctx) + vals = { + 'date_invoice': date_invoice, + 'move_id': move.id, + 'period_id': period.id, + 'move_name': move.name, + } + inv.with_context(ctx).write(vals) # Pass invoice in context in method post: used if you want to get the same # account move reference when creating the same invoice after a cancelled one: - move_obj.post(cr, uid, [move_id], context=ctx) - self._log_event(cr, uid, ids) + move.post() + self._log_event() return True - def invoice_validate(self, cr, uid, ids, context=None): - self.write(cr, uid, ids, {'state':'open'}, context=context) - return True + @api.multi + def invoice_validate(self): + return self.write({'state': 'open'}) - def line_get_convert(self, cr, uid, x, part, date, context=None): + @api.model + def line_get_convert(self, line, part, date): return { - 'date_maturity': x.get('date_maturity', False), + 'date_maturity': line.get('date_maturity', False), 'partner_id': part, - 'name': x['name'][:64], + 'name': line['name'][:64], 'date': date, - 'debit': x['price']>0 and x['price'], - 'credit': x['price']<0 and -x['price'], - 'account_id': x['account_id'], - 'analytic_lines': x.get('analytic_lines', []), - 'amount_currency': x['price']>0 and abs(x.get('amount_currency', False)) or -abs(x.get('amount_currency', False)), - 'currency_id': x.get('currency_id', False), - 'tax_code_id': x.get('tax_code_id', False), - 'tax_amount': x.get('tax_amount', False), - 'ref': x.get('ref', False), - 'quantity': x.get('quantity',1.00), - 'product_id': x.get('product_id', False), - 'product_uom_id': x.get('uos_id', False), - 'analytic_account_id': x.get('account_analytic_id', False), + 'debit': line['price']>0 and line['price'], + 'credit': line['price']<0 and -line['price'], + 'account_id': line['account_id'], + 'analytic_lines': line.get('analytic_lines', []), + 'amount_currency': line['price']>0 and abs(line.get('amount_currency', False)) or -abs(line.get('amount_currency', False)), + 'currency_id': line.get('currency_id', False), + 'tax_code_id': line.get('tax_code_id', False), + 'tax_amount': line.get('tax_amount', False), + 'ref': line.get('ref', False), + 'quantity': line.get('quantity',1.00), + 'product_id': line.get('product_id', False), + 'product_uom_id': line.get('uos_id', False), + 'analytic_account_id': line.get('account_analytic_id', False), } - def action_number(self, cr, uid, ids, context=None): - if context is None: - context = {} - #TODO: not correct fix but required a frech values before reading it. - self.write(cr, uid, ids, {}) + @api.multi + def action_number(self): + #TODO: not correct fix but required a fresh values before reading it. + self.write({}) - for obj_inv in self.browse(cr, uid, ids, context=context): - invtype = obj_inv.type - number = obj_inv.number - move_id = obj_inv.move_id and obj_inv.move_id.id or False - reference = obj_inv.reference or '' + for inv in self: + self.write({'internal_number': inv.number}) - self.write(cr, uid, ids, {'internal_number': number}) - - if invtype in ('in_invoice', 'in_refund'): - if not reference: - ref = self._convert_ref(cr, uid, number) + if inv.type in ('in_invoice', 'in_refund'): + if not inv.reference: + ref = self._convert_ref(inv.number) else: - ref = reference + ref = inv.reference else: - ref = self._convert_ref(cr, uid, number) + ref = self._convert_ref(inv.number) + + self._cr.execute(""" UPDATE account_move SET ref=%s + WHERE id=%s AND (ref IS NULL OR ref = '')""", + (ref, inv.move_id.id)) + self._cr.execute(""" UPDATE account_move_line SET ref=%s + WHERE move_id=%s AND (ref IS NULL OR ref = '')""", + (ref, inv.move_id.id)) + self._cr.execute(""" UPDATE account_analytic_line SET ref=%s + FROM account_move_line + WHERE account_move_line.move_id = %s AND + account_analytic_line.move_id = account_move_line.id""", + (ref, inv.move_id.id)) + self.invalidate_cache() - cr.execute('UPDATE account_move SET ref=%s ' \ - 'WHERE id=%s AND (ref is null OR ref = \'\')', - (ref, move_id)) - cr.execute('UPDATE account_move_line SET ref=%s ' \ - 'WHERE move_id=%s AND (ref is null OR ref = \'\')', - (ref, move_id)) - cr.execute('UPDATE account_analytic_line SET ref=%s ' \ - 'FROM account_move_line ' \ - 'WHERE account_move_line.move_id = %s ' \ - 'AND account_analytic_line.move_id = account_move_line.id', - (ref, move_id)) return True - def action_cancel(self, cr, uid, ids, context=None): - if context is None: - context = {} - account_move_obj = self.pool.get('account.move') - invoices = self.read(cr, uid, ids, ['move_id', 'payment_ids']) - move_ids = [] # ones that we will need to remove - for i in invoices: - if i['move_id']: - move_ids.append(i['move_id'][0]) - if i['payment_ids']: - account_move_line_obj = self.pool.get('account.move.line') - pay_ids = account_move_line_obj.browse(cr, uid, i['payment_ids']) - for move_line in pay_ids: - if move_line.reconcile_partial_id and move_line.reconcile_partial_id.line_partial_ids: - raise osv.except_osv(_('Error!'), _('You cannot cancel an invoice which is partially paid. You need to unreconcile related payment entries first.')) + @api.multi + def action_cancel(self): + moves = self.env['account.move'] + for inv in self: + if inv.move_id: + moves += inv.move_id + if inv.payment_ids: + for move_line in inv.payment_ids: + if move_line.reconcile_partial_id.line_partial_ids: + raise except_orm(_('Error!'), _('You cannot cancel an invoice which is partially paid. You need to unreconcile related payment entries first.')) # First, set the invoices as cancelled and detach the move ids - self.write(cr, uid, ids, {'state':'cancel', 'move_id':False}) - if move_ids: + self.write({'state': 'cancel', 'move_id': False}) + if moves: # second, invalidate the move(s) - account_move_obj.button_cancel(cr, uid, move_ids, context=context) + moves.button_cancel() # delete the move this invoice was pointing to # Note that the corresponding move_lines and move_reconciles # will be automatically deleted too - account_move_obj.unlink(cr, uid, move_ids, context=context) - self._log_event(cr, uid, ids, -1.0, 'Cancel Invoice') + moves.unlink() + self._log_event(-1.0, 'Cancel Invoice') return True ################### - def list_distinct_taxes(self, cr, uid, ids): - invoices = self.browse(cr, uid, ids) - taxes = {} - for inv in invoices: - for tax in inv.tax_line: - if not tax['name'] in taxes: - taxes[tax['name']] = {'name': tax['name']} - return taxes.values() - - def _log_event(self, cr, uid, ids, factor=1.0, name='Open Invoice'): + @api.multi + def _log_event(self, factor=1.0, name='Open Invoice'): #TODO: implement messages system return True - def name_get(self, cr, uid, ids, context=None): - if not ids: - return [] - types = { - 'out_invoice': _('Invoice'), - 'in_invoice': _('Supplier Invoice'), - 'out_refund': _('Refund'), - 'in_refund': _('Supplier Refund'), - } - return [(r['id'], '%s %s' % (r['number'] or types[r['type']], r['name'] or '')) for r in self.read(cr, uid, ids, ['type', 'number', 'name'], context, load='_classic_write')] + @api.one + def _compute_display_name(self): + TYPES = { + 'out_invoice': _('Invoice'), + 'in_invoice': _('Supplier Invoice'), + 'out_refund': _('Refund'), + 'in_refund': _('Supplier Refund'), + } + self.display_name = "%s %s" % (self.number or TYPES[self.type], self.name or '') - def name_search(self, cr, user, name, args=None, operator='ilike', context=None, limit=100): - if not args: - args = [] - if context is None: - context = {} - ids = [] + @api.model + def name_search(self, name, args=None, operator='ilike', limit=100): + args = args or [] + recs = self.browse() if name: - ids = self.search(cr, user, [('number','=',name)] + args, limit=limit, context=context) - if not ids: - ids = self.search(cr, user, [('name',operator,name)] + args, limit=limit, context=context) - return self.name_get(cr, user, ids, context) + recs = self.search([('number', '=', name)] + args, limit=limit) + if not recs: + recs = self.search([('name', operator, name)] + args, limit=limit) + return recs.name_get() - def _refund_cleanup_lines(self, cr, uid, lines, context=None): - """Convert records to dict of values suitable for one2many line creation + @api.model + def _refund_cleanup_lines(self, lines): + """ Convert records to dict of values suitable for one2many line creation - :param list(browse_record) lines: records to convert + :param recordset lines: records to convert :return: list of command tuple for one2many line creation [(0, 0, dict of valueis), ...] """ - clean_lines = [] + result = [] for line in lines: - clean_line = {} - for field in line._all_columns.keys(): - if line._all_columns[field].column._type == 'many2one': - clean_line[field] = line[field].id - elif line._all_columns[field].column._type not in ['many2many','one2many']: - clean_line[field] = line[field] - elif field == 'invoice_line_tax_id': - tax_list = [] - for tax in line[field]: - tax_list.append(tax.id) - clean_line[field] = [(6,0, tax_list)] - clean_lines.append(clean_line) - return map(lambda x: (0,0,x), clean_lines) + values = {} + for name, field in line._fields.iteritems(): + if name in MAGIC_COLUMNS: + continue + elif field.type == 'many2one': + values[name] = line[name].id + elif field.type not in ['many2many', 'one2many']: + values[name] = line[name] + elif name == 'invoice_line_tax_id': + values[name] = [(6, 0, line[name].ids)] + result.append((0, 0, values)) + return result - def _prepare_refund(self, cr, uid, invoice, date=None, period_id=None, description=None, journal_id=None, context=None): - """Prepare the dict of values to create the new refund from the invoice. + @api.model + def _prepare_refund(self, invoice, date=None, period_id=None, description=None, journal_id=None): + """ Prepare the dict of values to create the new refund from the invoice. This method may be overridden to implement custom refund generation (making sure to call super() to establish a clean extension chain). - :param integer invoice_id: id of the invoice to refund - :param dict invoice: read of the invoice to refund + :param record invoice: invoice to refund :param string date: refund creation date from the wizard :param integer period_id: force account.period from the wizard :param string description: description of the refund from the wizard :param integer journal_id: account.journal from the wizard :return: dict of value to create() the refund """ - obj_journal = self.pool.get('account.journal') - - type_dict = { - 'out_invoice': 'out_refund', # Customer Invoice - 'in_invoice': 'in_refund', # Supplier Invoice - 'out_refund': 'out_invoice', # Customer Refund - 'in_refund': 'in_invoice', # Supplier Refund - } - invoice_data = {} + values = {} for field in ['name', 'reference', 'comment', 'date_due', 'partner_id', 'company_id', 'account_id', 'currency_id', 'payment_term', 'user_id', 'fiscal_position']: - if invoice._all_columns[field].column._type == 'many2one': - invoice_data[field] = invoice[field].id + if invoice._fields[field].type == 'many2one': + values[field] = invoice[field].id else: - invoice_data[field] = invoice[field] if invoice[field] else False + values[field] = invoice[field] or False - invoice_lines = self._refund_cleanup_lines(cr, uid, invoice.invoice_line, context=context) + values['invoice_line'] = self._refund_cleanup_lines(invoice.invoice_line) + + tax_lines = filter(lambda l: l.manual, invoice.tax_line) + values['tax_line'] = self._refund_cleanup_lines(tax_lines) - tax_lines = filter(lambda l: l['manual'], invoice.tax_line) - tax_lines = self._refund_cleanup_lines(cr, uid, tax_lines, context=context) if journal_id: - refund_journal_ids = [journal_id] + journal = self.env['account.journal'].browse(journal_id) elif invoice['type'] == 'in_invoice': - refund_journal_ids = obj_journal.search(cr, uid, [('type','=','purchase_refund')], context=context) + journal = self.env['account.journal'].search([('type', '=', 'purchase_refund')], limit=1) else: - refund_journal_ids = obj_journal.search(cr, uid, [('type','=','sale_refund')], context=context) + journal = self.env['account.journal'].search([('type', '=', 'sale_refund')], limit=1) + values['journal_id'] = journal.id + + values['type'] = TYPE2REFUND[invoice['type']] + values['date_invoice'] = date or fields.Date.today() + values['state'] = 'draft' + values['number'] = False - if not date: - date = time.strftime('%Y-%m-%d') - invoice_data.update({ - 'type': type_dict[invoice['type']], - 'date_invoice': date, - 'state': 'draft', - 'number': False, - 'invoice_line': invoice_lines, - 'tax_line': tax_lines, - 'journal_id': refund_journal_ids and refund_journal_ids[0] or False, - }) if period_id: - invoice_data['period_id'] = period_id + values['period_id'] = period_id if description: - invoice_data['name'] = description - return invoice_data + values['name'] = description + return values - def refund(self, cr, uid, ids, date=None, period_id=None, description=None, journal_id=None, context=None): - new_ids = [] - for invoice in self.browse(cr, uid, ids, context=context): - invoice = self._prepare_refund(cr, uid, invoice, - date=date, - period_id=period_id, - description=description, - journal_id=journal_id, - context=context) + @api.multi + @api.returns('self') + def refund(self, date=None, period_id=None, description=None, journal_id=None): + new_invoices = self.browse() + for invoice in self: # create the new invoice - new_ids.append(self.create(cr, uid, invoice, context=context)) + values = self._prepare_refund(invoice, date=date, period_id=period_id, + description=description, journal_id=journal_id) + new_invoices += self.create(values) + return new_invoices - return new_ids - - def pay_and_reconcile(self, cr, uid, ids, pay_amount, pay_account_id, period_id, pay_journal_id, writeoff_acc_id, writeoff_period_id, writeoff_journal_id, context=None, name=''): - if context is None: - context = {} - #TODO check if we can use different period for payment and the writeoff line - assert len(ids)==1, "Can only pay one invoice at a time." - invoice = self.browse(cr, uid, ids[0], context=context) - src_account_id = invoice.account_id.id + @api.v8 + def pay_and_reconcile(self, pay_amount, pay_account_id, period_id, pay_journal_id, + writeoff_acc_id, writeoff_period_id, writeoff_journal_id, name=''): + # TODO check if we can use different period for payment and the writeoff line + assert len(self)==1, "Can only pay one invoice at a time." # Take the seq as name for move - types = {'out_invoice': -1, 'in_invoice': 1, 'out_refund': 1, 'in_refund': -1} - direction = types[invoice.type] - #take the choosen date - if 'date_p' in context and context['date_p']: - date=context['date_p'] - else: - date=time.strftime('%Y-%m-%d') + SIGN = {'out_invoice': -1, 'in_invoice': 1, 'out_refund': 1, 'in_refund': -1} + direction = SIGN[self.type] + # take the chosen date + date = self._context.get('date_p') or fields.Date.today() # Take the amount in currency and the currency of the payment - if 'amount_currency' in context and context['amount_currency'] and 'currency_id' in context and context['currency_id']: - amount_currency = context['amount_currency'] - currency_id = context['currency_id'] + if self._context.get('amount_currency') and self._context.get('currency_id'): + amount_currency = self._context['amount_currency'] + currency_id = self._context['currency_id'] else: amount_currency = False currency_id = False - if invoice.type in ('in_invoice', 'in_refund'): - ref = invoice.reference + pay_journal = self.env['account.journal'].browse(pay_journal_id) + if self.type in ('in_invoice', 'in_refund'): + ref = self.reference else: - ref = self._convert_ref(cr, uid, invoice.number) - partner = self.pool['res.partner']._find_accounting_partner(invoice.partner_id) + ref = self._convert_ref(self.number) + partner = self.partner_id._find_accounting_partner(self.partner_id) + name = name or self.invoice_line.name or self.number # Pay attention to the sign for both debit/credit AND amount_currency l1 = { - 'debit': direction * pay_amount>0 and direction * pay_amount, - 'credit': direction * pay_amount<0 and - direction * pay_amount, - 'account_id': src_account_id, + 'name': name, + 'debit': direction * pay_amount > 0 and direction * pay_amount, + 'credit': direction * pay_amount < 0 and -direction * pay_amount, + 'account_id': self.account_id.id, 'partner_id': partner.id, - 'ref':ref, + 'ref': ref, 'date': date, - 'currency_id':currency_id, - 'amount_currency':amount_currency and direction * amount_currency or 0.0, - 'company_id': invoice.company_id.id, + 'currency_id': currency_id, + 'amount_currency': direction * (amount_currency or 0.0), + 'company_id': self.company_id.id, } l2 = { - 'debit': direction * pay_amount<0 and - direction * pay_amount, - 'credit': direction * pay_amount>0 and direction * pay_amount, + 'name': name, + 'debit': direction * pay_amount < 0 and -direction * pay_amount, + 'credit': direction * pay_amount > 0 and direction * pay_amount, 'account_id': pay_account_id, 'partner_id': partner.id, - 'ref':ref, + 'ref': ref, 'date': date, - 'currency_id':currency_id, - 'amount_currency':amount_currency and - direction * amount_currency or 0.0, - 'company_id': invoice.company_id.id, + 'currency_id': currency_id, + 'amount_currency': -direction * (amount_currency or 0.0), + 'company_id': self.company_id.id, } + move = self.env['account.move'].create({ + 'ref': ref, + 'line_id': [(0, 0, l1), (0, 0, l2)], + 'journal_id': pay_journal_id, + 'period_id': period_id, + 'date': date, + }) - if not name: - name = invoice.invoice_line and invoice.invoice_line[0].name or invoice.number - l1['name'] = name - l2['name'] = name - - lines = [(0, 0, l1), (0, 0, l2)] - move = {'ref': ref, 'line_id': lines, 'journal_id': pay_journal_id, 'period_id': period_id, 'date': date} - move_id = self.pool.get('account.move').create(cr, uid, move, context=context) - - line_ids = [] + move_ids = (move | self.move_id).ids + self._cr.execute("SELECT id FROM account_move_line WHERE move_id IN %s", + (tuple(move_ids),)) + lines = self.env['account.move.line'].browse([r[0] for r in self._cr.fetchall()]) + lines2rec = lines.browse() total = 0.0 - line = self.pool.get('account.move.line') - move_ids = [move_id,] - if invoice.move_id: - move_ids.append(invoice.move_id.id) - cr.execute('SELECT id FROM account_move_line '\ - 'WHERE move_id IN %s', - ((move_id, invoice.move_id.id),)) - lines = line.browse(cr, uid, map(lambda x: x[0], cr.fetchall()) ) - for l in lines+invoice.payment_ids: - if l.account_id.id == src_account_id: - line_ids.append(l.id) - total += (l.debit or 0.0) - (l.credit or 0.0) + for line in itertools.chain(lines, self.payment_ids): + if line.account_id == self.account_id: + lines2rec += line + total += (line.debit or 0.0) - (line.credit or 0.0) - inv_id, name = self.name_get(cr, uid, [invoice.id], context=context)[0] - if (not round(total,self.pool.get('decimal.precision').precision_get(cr, uid, 'Account'))) or writeoff_acc_id: - self.pool.get('account.move.line').reconcile(cr, uid, line_ids, 'manual', writeoff_acc_id, writeoff_period_id, writeoff_journal_id, context) + inv_id, name = self.name_get()[0] + if not round(total, self.env['decimal.precision'].precision_get('Account')) or writeoff_acc_id: + lines2rec.reconcile('manual', writeoff_acc_id, writeoff_period_id, writeoff_journal_id) else: - code = invoice.currency_id.symbol + code = self.currency_id.symbol # TODO: use currency's formatting function msg = _("Invoice partially paid: %s%s of %s%s (%s%s remaining).") % \ - (pay_amount, code, invoice.amount_total, code, total, code) - self.message_post(cr, uid, [inv_id], body=msg, context=context) - self.pool.get('account.move.line').reconcile_partial(cr, uid, line_ids, 'manual', context) + (pay_amount, code, self.amount_total, code, total, code) + self.message_post(body=msg) + lines2rec.reconcile_partial('manual') # Update the stored value (fields.function), so we write to trigger recompute - self.pool.get('account.invoice').write(cr, uid, ids, {}, context=context) - return True + return self.write({}) + @api.v7 + def pay_and_reconcile(self, cr, uid, ids, pay_amount, pay_account_id, period_id, pay_journal_id, + writeoff_acc_id, writeoff_period_id, writeoff_journal_id, context=None, name=''): + recs = self.browse(cr, uid, ids, context) + return recs.pay_and_reconcile(pay_amount, pay_account_id, period_id, pay_journal_id, + writeoff_acc_id, writeoff_period_id, writeoff_journal_id, name=name) -class account_invoice_line(osv.osv): - - def _amount_line(self, cr, uid, ids, prop, unknow_none, unknow_dict): - res = {} - tax_obj = self.pool.get('account.tax') - cur_obj = self.pool.get('res.currency') - for line in self.browse(cr, uid, ids): - price = line.price_unit * (1-(line.discount or 0.0)/100.0) - taxes = tax_obj.compute_all(cr, uid, line.invoice_line_tax_id, price, line.quantity, product=line.product_id, partner=line.invoice_id.partner_id) - res[line.id] = taxes['total'] - if line.invoice_id: - cur = line.invoice_id.currency_id - res[line.id] = cur_obj.round(cr, uid, cur, res[line.id]) - return res - - def _price_unit_default(self, cr, uid, context=None): - if context is None: - context = {} - if context.get('check_total', False): - t = context['check_total'] - for l in context.get('invoice_line', {}): - if isinstance(l, (list, tuple)) and len(l) >= 3 and l[2]: - tax_obj = self.pool.get('account.tax') - p = l[2].get('price_unit', 0) * (1-l[2].get('discount', 0)/100.0) - t = t - (p * l[2].get('quantity')) - taxes = l[2].get('invoice_line_tax_id') - if len(taxes[0]) >= 3 and taxes[0][2]: - taxes = tax_obj.browse(cr, uid, list(taxes[0][2])) - for tax in tax_obj.compute_all(cr, uid, taxes, p,l[2].get('quantity'), l[2].get('product_id', False), context.get('partner_id', False))['taxes']: - t = t - tax['amount'] - return t - return 0 - +class account_invoice_line(models.Model): _name = "account.invoice.line" _description = "Invoice Line" _order = "invoice_id,sequence,id" - _columns = { - 'name': fields.text('Description', required=True), - 'origin': fields.char('Source Document', help="Reference of the document that produced this invoice."), - 'sequence': fields.integer('Sequence', help="Gives the sequence of this line when displaying the invoice."), - 'invoice_id': fields.many2one('account.invoice', 'Invoice Reference', ondelete='cascade', select=True), - 'uos_id': fields.many2one('product.uom', 'Unit of Measure', ondelete='set null', select=True), - 'product_id': fields.many2one('product.product', 'Product', ondelete='set null', select=True), - 'account_id': fields.many2one('account.account', 'Account', required=True, domain=[('type','<>','view'), ('type', '<>', 'closed')], help="The income or expense account related to the selected product."), - 'price_unit': fields.float('Unit Price', required=True, digits_compute= dp.get_precision('Product Price')), - 'price_subtotal': fields.function(_amount_line, string='Amount', type="float", - digits_compute= dp.get_precision('Account'), store=True), - 'quantity': fields.float('Quantity', digits_compute= dp.get_precision('Product Unit of Measure'), required=True), - 'discount': fields.float('Discount (%)', digits_compute= dp.get_precision('Discount')), - 'invoice_line_tax_id': fields.many2many('account.tax', 'account_invoice_line_tax', 'invoice_line_id', 'tax_id', 'Taxes', domain=[('parent_id','=',False)]), - 'account_analytic_id': fields.many2one('account.analytic.account', 'Analytic Account'), - 'company_id': fields.related('invoice_id','company_id',type='many2one',relation='res.company',string='Company', store=True, readonly=True), - 'partner_id': fields.related('invoice_id','partner_id',type='many2one',relation='res.partner',string='Partner',store=True) - } - def _default_account_id(self, cr, uid, context=None): + @api.one + @api.depends('price_unit', 'discount', 'invoice_line_tax_id', 'quantity', + 'product_id', 'invoice_id.partner_id', 'invoice_id.currency_id') + def _compute_price(self): + price = self.price_unit * (1 - (self.discount or 0.0) / 100.0) + taxes = self.invoice_line_tax_id.compute_all(price, self.quantity, product=self.product_id, partner=self.invoice_id.partner_id) + self.price_subtotal = taxes['total'] + if self.invoice_id: + self.price_subtotal = self.invoice_id.currency_id.round(self.price_subtotal) + + @api.model + def _default_price_unit(self): + if not self._context.get('check_total'): + return 0 + total = self._context['check_total'] + for l in self._context.get('invoice_line', []): + if isinstance(l, (list, tuple)) and len(l) >= 3 and l[2]: + vals = l[2] + price = vals.get('price_unit', 0) * (1 - vals.get('discount', 0) / 100.0) + total = total - (price * vals.get('quantity')) + taxes = vals.get('invoice_line_tax_id') + if taxes and len(taxes[0]) >= 3 and taxes[0][2]: + taxes = self.env['account.tax'].browse(taxes[0][2]) + tax_res = taxes.compute_all(price, vals.get('quantity'), + product=vals.get('product_id'), partner=self._context.get('partner_id')) + for tax in tax_res['taxes']: + total = total - tax['amount'] + return total + + @api.model + def _default_account(self): # XXX this gets the default account for the user's company, # it should get the default account for the invoice's company # however, the invoice's company does not reach this point - if context is None: - context = {} - if context.get('type') in ('out_invoice','out_refund'): - prop = self.pool.get('ir.property').get(cr, uid, 'property_account_income_categ', 'product.category', context=context) + if self._context.get('type') in ('out_invoice', 'out_refund'): + return self.env['ir.property'].get('property_account_income_categ', 'product.category') else: - prop = self.pool.get('ir.property').get(cr, uid, 'property_account_expense_categ', 'product.category', context=context) - return prop and prop.id or False + return self.env['ir.property'].get('property_account_expense_categ', 'product.category') - _defaults = { - 'quantity': 1, - 'discount': 0.0, - 'price_unit': _price_unit_default, - 'account_id': _default_account_id, - 'sequence': 10, - } + name = fields.Text(string='Description', required=True) + origin = fields.Char(string='Source Document', + help="Reference of the document that produced this invoice.") + sequence = fields.Integer(string='Sequence', default=10, + help="Gives the sequence of this line when displaying the invoice.") + invoice_id = fields.Many2one('account.invoice', string='Invoice Reference', + ondelete='cascade', index=True) + uos_id = fields.Many2one('product.uom', string='Unit of Measure', + ondelete='set null', index=True) + product_id = fields.Many2one('product.product', string='Product', + ondelete='set null', index=True) + account_id = fields.Many2one('account.account', string='Account', + required=True, domain=[('type', 'not in', ['view', 'closed'])], + default=_default_account, + help="The income or expense account related to the selected product.") + price_unit = fields.Float(string='Unit Price', required=True, + digits= dp.get_precision('Product Price'), + default=_default_price_unit) + price_subtotal = fields.Float(string='Amount', digits= dp.get_precision('Account'), + store=True, readonly=True, compute='_compute_price') + quantity = fields.Float(string='Quantity', digits= dp.get_precision('Product Unit of Measure'), + required=True, default=1) + discount = fields.Float(string='Discount (%)', digits= dp.get_precision('Discount'), + default=0.0) + invoice_line_tax_id = fields.Many2many('account.tax', + 'account_invoice_line_tax', 'invoice_line_id', 'tax_id', + string='Taxes', domain=[('parent_id', '=', False)]) + account_analytic_id = fields.Many2one('account.analytic.account', + string='Analytic Account') + company_id = fields.Many2one('res.company', string='Company', + related='invoice_id.company_id', store=True, readonly=True) + partner_id = fields.Many2one('res.partner', string='Partner', + related='invoice_id.partner_id', store=True, readonly=True) - def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False): - if context is None: - context = {} - res = super(account_invoice_line,self).fields_view_get(cr, uid, view_id=view_id, view_type=view_type, context=context, toolbar=toolbar, submenu=submenu) - if context.get('type', False): + @api.model + def fields_view_get(self, view_id=None, view_type='form', toolbar=False, submenu=False): + res = super(account_invoice_line, self).fields_view_get( + view_id=view_id, view_type=view_type, toolbar=toolbar, submenu=submenu) + if self._context.get('type'): doc = etree.XML(res['arch']) for node in doc.xpath("//field[@name='product_id']"): - if context['type'] in ('in_invoice', 'in_refund'): + if self._context['type'] in ('in_invoice', 'in_refund'): node.set('domain', "[('purchase_ok', '=', True)]") else: node.set('domain', "[('sale_ok', '=', True)]") res['arch'] = etree.tostring(doc) return res - def product_id_change(self, cr, uid, ids, product, uom_id, qty=0, name='', type='out_invoice', partner_id=False, fposition_id=False, price_unit=False, currency_id=False, context=None, company_id=None): - if context is None: - context = {} - company_id = company_id if company_id != None else context.get('company_id',False) - context = dict(context) - context.update({'company_id': company_id, 'force_company': company_id}) + @api.multi + def product_id_change(self, product, uom_id, qty=0, name='', type='out_invoice', + partner_id=False, fposition_id=False, price_unit=False, currency_id=False, + context=None, company_id=None): + context = context or {} + company_id = company_id if company_id is not None else context.get('company_id', False) + self = self.with_context(company_id=company_id, force_company=company_id) + if not partner_id: - raise osv.except_osv(_('No Partner Defined!'),_("You must first select a partner!") ) + raise except_orm(_('No Partner Defined!'), _("You must first select a partner!")) if not product: if type in ('in_invoice', 'in_refund'): - return {'value': {}, 'domain':{'product_uom':[]}} + return {'value': {}, 'domain': {'product_uom': []}} else: - return {'value': {'price_unit': 0.0}, 'domain':{'product_uom':[]}} - part = self.pool.get('res.partner').browse(cr, uid, partner_id, context=context) - fpos_obj = self.pool.get('account.fiscal.position') - fpos = fposition_id and fpos_obj.browse(cr, uid, fposition_id, context=context) or False + return {'value': {'price_unit': 0.0}, 'domain': {'product_uom': []}} + + values = {} + + part = self.env['res.partner'].browse(partner_id) + fpos = self.env['account.fiscal.position'].browse(fposition_id) if part.lang: - context.update({'lang': part.lang}) - result = {} - res = self.pool.get('product.product').browse(cr, uid, product, context=context) + self = self.with_context(lang=part.lang) + product = self.env['product.product'].browse(product) - result['name'] = res.partner_ref - if type in ('out_invoice','out_refund'): - a = res.property_account_income.id - if not a: - a = res.categ_id.property_account_income_categ.id + values['name'] = product.partner_ref + if type in ('out_invoice', 'out_refund'): + account = product.property_account_income or product.categ_id.property_account_income_categ else: - a = res.property_account_expense.id - if not a: - a = res.categ_id.property_account_expense_categ.id - a = fpos_obj.map_account(cr, uid, fpos, a) - if a: - result['account_id'] = a + account = product.property_account_expense or product.categ_id.property_account_expense_categ + account = fpos.map_account(account) + if account: + values['account_id'] = account.id if type in ('out_invoice', 'out_refund'): - taxes = res.taxes_id and res.taxes_id or (a and self.pool.get('account.account').browse(cr, uid, a, context=context).tax_ids or False) - if res.description_sale: - result['name'] += '\n'+res.description_sale + taxes = product.taxes_id or account.tax_ids + if product.description_sale: + values['name'] += '\n' + product.description_sale else: - taxes = res.supplier_taxes_id and res.supplier_taxes_id or (a and self.pool.get('account.account').browse(cr, uid, a, context=context).tax_ids or False) - if res.description_purchase: - result['name'] += '\n'+res.description_purchase + taxes = product.supplier_taxes_id or account.tax_ids + if product.description_purchase: + values['name'] += '\n' + product.description_purchase - tax_id = fpos_obj.map_tax(cr, uid, fpos, taxes) + taxes = fpos.map_tax(taxes) + values['invoice_line_tax_id'] = taxes.ids if type in ('in_invoice', 'in_refund'): - result.update( {'price_unit': price_unit or res.standard_price,'invoice_line_tax_id': tax_id} ) + values['price_unit'] = price_unit or product.standard_price else: - result.update({'price_unit': res.list_price, 'invoice_line_tax_id': tax_id}) + values['price_unit'] = product.list_price - result['uos_id'] = uom_id or res.uom_id.id + values['uos_id'] = uom_id or product.uom_id.id + domain = {'uos_id': [('category_id', '=', product.uom_id.category_id.id)]} - domain = {'uos_id':[('category_id','=',res.uom_id.category_id.id)]} + company = self.env['res.company'].browse(company_id) + currency = self.env['res.currency'].browse(currency_id) - res_final = {'value':result, 'domain':domain} + if company and currency: + if company.currency_id != currency: + if type in ('in_invoice', 'in_refund'): + values['price_unit'] = product.standard_price + values['price_unit'] = values['price_unit'] * currency.rate - if not company_id or not currency_id: - return res_final + if values['uos_id'] and values['uos_id'] != product.uom_id.id: + values['price_unit'] = self.env['product.uom']._compute_price( + product.uom_id.id, values['price_unit'], values['uos_id']) - company = self.pool.get('res.company').browse(cr, uid, company_id, context=context) - currency = self.pool.get('res.currency').browse(cr, uid, currency_id, context=context) + return {'value': values, 'domain': domain} - if company.currency_id.id != currency.id: - if type in ('in_invoice', 'in_refund'): - res_final['value']['price_unit'] = res.standard_price - new_price = res_final['value']['price_unit'] * currency.rate - res_final['value']['price_unit'] = new_price + @api.multi + def uos_id_change(self, product, uom, qty=0, name='', type='out_invoice', partner_id=False, + fposition_id=False, price_unit=False, currency_id=False, context=None, company_id=None): + context = context or {} + company_id = company_id if company_id != None else context.get('company_id', False) + self = self.with_context(company_id=company_id) - if result['uos_id'] and result['uos_id'] != res.uom_id.id: - new_price = self.pool.get('product.uom')._compute_price(cr, uid, res.uom_id.id, res_final['value']['price_unit'], result['uos_id']) - res_final['value']['price_unit'] = new_price - return res_final - - def uos_id_change(self, cr, uid, ids, product, uom, qty=0, name='', type='out_invoice', partner_id=False, fposition_id=False, price_unit=False, currency_id=False, context=None, company_id=None): - if context is None: - context = {} - company_id = company_id if company_id != None else context.get('company_id',False) - context = dict(context) - context.update({'company_id': company_id}) + result = self.product_id_change( + product, uom, qty, name, type, partner_id, fposition_id, price_unit, + currency_id, context=context, company_id=company_id, + ) warning = {} - res = self.product_id_change(cr, uid, ids, product, uom, qty, name, type, partner_id, fposition_id, price_unit, currency_id, context=context, company_id=company_id) if not uom: - res['value']['price_unit'] = 0.0 + result['value']['price_unit'] = 0.0 if product and uom: - prod = self.pool.get('product.product').browse(cr, uid, product, context=context) - prod_uom = self.pool.get('product.uom').browse(cr, uid, uom, context=context) - if prod.uom_id.category_id.id != prod_uom.category_id.id: + prod = self.env['product.product'].browse(product) + prod_uom = self.env['product.uom'].browse(uom) + if prod.uom_id.category_id != prod_uom.category_id: warning = { 'title': _('Warning!'), - 'message': _('The selected unit of measure is not compatible with the unit of measure of the product.') + 'message': _('The selected unit of measure is not compatible with the unit of measure of the product.'), } - res['value'].update({'uos_id': prod.uom_id.id}) - return {'value': res['value'], 'warning': warning} - return res + result['value']['uos_id'] = prod.uom_id.id + if warning: + result['warning'] = warning + return result + + @api.model + def move_line_get(self, invoice_id): + inv = self.env['account.invoice'].browse(invoice_id) + currency = inv.currency_id.with_context(date=inv.date_invoice) + company_currency = inv.company_id.currency_id - def move_line_get(self, cr, uid, invoice_id, context=None): res = [] - tax_obj = self.pool.get('account.tax') - cur_obj = self.pool.get('res.currency') - if context is None: - context = {} - inv = self.pool.get('account.invoice').browse(cr, uid, invoice_id, context=context) - company_currency = self.pool['res.company'].browse(cr, uid, inv.company_id.id).currency_id.id for line in inv.invoice_line: - mres = self.move_line_get_item(cr, uid, line, context) + mres = self.move_line_get_item(line) if not mres: continue res.append(mres) - tax_code_found= False - for tax in tax_obj.compute_all(cr, uid, line.invoice_line_tax_id, - (line.price_unit * (1.0 - (line['discount'] or 0.0) / 100.0)), - line.quantity, line.product_id, - inv.partner_id)['taxes']: - + tax_code_found = False + taxes = line.invoice_line_tax_id.compute_all( + (line.price_unit * (1.0 - (line.discount or 0.0) / 100.0)), + line.quantity, line.product_id, inv.partner_id)['taxes'] + for tax in taxes: if inv.type in ('out_invoice', 'in_invoice'): tax_code_id = tax['base_code_id'] tax_amount = line.price_subtotal * tax['base_sign'] @@ -1587,7 +1410,7 @@ class account_invoice_line(osv.osv): if tax_code_found: if not tax_code_id: continue - res.append(self.move_line_get_item(cr, uid, line, context)) + res.append(dict(mres)) res[-1]['price'] = 0.0 res[-1]['account_analytic_id'] = False elif not tax_code_id: @@ -1595,143 +1418,134 @@ class account_invoice_line(osv.osv): tax_code_found = True res[-1]['tax_code_id'] = tax_code_id - res[-1]['tax_amount'] = cur_obj.compute(cr, uid, inv.currency_id.id, company_currency, tax_amount, context={'date': inv.date_invoice}) + res[-1]['tax_amount'] = currency.compute(tax_amount, company_currency) + return res - def move_line_get_item(self, cr, uid, line, context=None): + @api.model + def move_line_get_item(self, line): return { - 'type':'src', + 'type': 'src', 'name': line.name.split('\n')[0][:64], - 'price_unit':line.price_unit, - 'quantity':line.quantity, - 'price':line.price_subtotal, - 'account_id':line.account_id.id, - 'product_id':line.product_id.id, - 'uos_id':line.uos_id.id, - 'account_analytic_id':line.account_analytic_id.id, - 'taxes':line.invoice_line_tax_id, + 'price_unit': line.price_unit, + 'quantity': line.quantity, + 'price': line.price_subtotal, + 'account_id': line.account_id.id, + 'product_id': line.product_id.id, + 'uos_id': line.uos_id.id, + 'account_analytic_id': line.account_analytic_id.id, + 'taxes': line.invoice_line_tax_id, } + # # Set the tax field according to the account and the fiscal position # - def onchange_account_id(self, cr, uid, ids, product_id, partner_id, inv_type, fposition_id, account_id): + @api.multi + def onchange_account_id(self, product_id, partner_id, inv_type, fposition_id, account_id): if not account_id: return {} unique_tax_ids = [] - fpos = fposition_id and self.pool.get('account.fiscal.position').browse(cr, uid, fposition_id) or False - account = self.pool.get('account.account').browse(cr, uid, account_id) + account = self.env['account.account'].browse(account_id) if not product_id: - taxes = account.tax_ids - unique_tax_ids = self.pool.get('account.fiscal.position').map_tax(cr, uid, fpos, taxes) + fpos = self.env['account.fiscal.position'].browse(fposition_id) + unique_tax_ids = fpos.map_tax(account.tax_ids).ids else: - product_change_result = self.product_id_change(cr, uid, ids, product_id, False, type=inv_type, - partner_id=partner_id, fposition_id=fposition_id, - company_id=account.company_id.id) - if product_change_result and 'value' in product_change_result and 'invoice_line_tax_id' in product_change_result['value']: + product_change_result = self.product_id_change(product_id, False, type=inv_type, + partner_id=partner_id, fposition_id=fposition_id, company_id=account.company_id.id) + if 'invoice_line_tax_id' in product_change_result.get('value', {}): unique_tax_ids = product_change_result['value']['invoice_line_tax_id'] - return {'value':{'invoice_line_tax_id': unique_tax_ids}} + return {'value': {'invoice_line_tax_id': unique_tax_ids}} -class account_invoice_tax(osv.osv): +class account_invoice_tax(models.Model): _name = "account.invoice.tax" _description = "Invoice Tax" + _order = 'sequence' - def _count_factor(self, cr, uid, ids, name, args, context=None): - res = {} - for invoice_tax in self.browse(cr, uid, ids, context=context): - res[invoice_tax.id] = { - 'factor_base': 1.0, - 'factor_tax': 1.0, - } - if invoice_tax.amount <> 0.0: - factor_tax = invoice_tax.tax_amount / invoice_tax.amount - res[invoice_tax.id]['factor_tax'] = factor_tax + @api.one + @api.depends('base', 'base_amount', 'amount', 'tax_amount') + def _compute_factors(self): + self.factor_base = self.base_amount / self.base if self.base else 1.0 + self.factor_tax = self.tax_amount / self.amount if self.amount else 1.0 - if invoice_tax.base <> 0.0: - factor_base = invoice_tax.base_amount / invoice_tax.base - res[invoice_tax.id]['factor_base'] = factor_base + invoice_id = fields.Many2one('account.invoice', string='Invoice Line', + ondelete='cascade', index=True) + name = fields.Char(string='Tax Description', + required=True) + account_id = fields.Many2one('account.account', string='Tax Account', + required=True, domain=[('type', 'not in', ['view', 'income', 'closed'])]) + account_analytic_id = fields.Many2one('account.analytic.account', string='Analytic account') + base = fields.Float(string='Base', digits=dp.get_precision('Account')) + amount = fields.Float(string='Amount', digits=dp.get_precision('Account')) + manual = fields.Boolean(string='Manual', default=True) + sequence = fields.Integer(string='Sequence', + help="Gives the sequence order when displaying a list of invoice tax.") + base_code_id = fields.Many2one('account.tax.code', string='Base Code', + help="The account basis of the tax declaration.") + base_amount = fields.Float(string='Base Code Amount', digits=dp.get_precision('Account'), + default=0.0) + tax_code_id = fields.Many2one('account.tax.code', string='Tax Code', + help="The tax basis of the tax declaration.") + tax_amount = fields.Float(string='Tax Code Amount', digits=dp.get_precision('Account'), + default=0.0) - return res + company_id = fields.Many2one('res.company', string='Company', + related='account_id.company_id', store=True, readonly=True) + factor_base = fields.Float(string='Multipication factor for Base code', + compute='_compute_factors') + factor_tax = fields.Float(string='Multipication factor Tax code', + compute='_compute_factors') - _columns = { - 'invoice_id': fields.many2one('account.invoice', 'Invoice Line', ondelete='cascade', select=True), - 'name': fields.char('Tax Description', required=True), - 'account_id': fields.many2one('account.account', 'Tax Account', required=True, domain=[('type','<>','view'),('type','<>','income'), ('type', '<>', 'closed')]), - 'account_analytic_id': fields.many2one('account.analytic.account', 'Analytic account'), - 'base': fields.float('Base', digits_compute=dp.get_precision('Account')), - 'amount': fields.float('Amount', digits_compute=dp.get_precision('Account')), - 'manual': fields.boolean('Manual'), - 'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of invoice tax."), - 'base_code_id': fields.many2one('account.tax.code', 'Base Code', help="The account basis of the tax declaration."), - 'base_amount': fields.float('Base Code Amount', digits_compute=dp.get_precision('Account')), - 'tax_code_id': fields.many2one('account.tax.code', 'Tax Code', help="The tax basis of the tax declaration."), - 'tax_amount': fields.float('Tax Code Amount', digits_compute=dp.get_precision('Account')), - 'company_id': fields.related('account_id', 'company_id', type='many2one', relation='res.company', string='Company', store=True, readonly=True), - 'factor_base': fields.function(_count_factor, string='Multipication factor for Base code', type='float', multi="all"), - 'factor_tax': fields.function(_count_factor, string='Multipication factor Tax code', type='float', multi="all") - } + @api.multi + def base_change(self, base, currency_id=False, company_id=False, date_invoice=False): + factor = self.factor_base if self else 1 + company = self.env['res.company'].browse(company_id) + if currency_id and company.currency_id: + currency = self.env['res.currency'].browse(currency_id) + currency = currency.with_context(date=date_invoice or fields.Date.today()) + base = currency.compute(base * factor, company.currency_id, round=False) + return {'value': {'base_amount': base}} - def base_change(self, cr, uid, ids, base, currency_id=False, company_id=False, date_invoice=False): - cur_obj = self.pool.get('res.currency') - company_obj = self.pool.get('res.company') - company_currency = False - factor = 1 - if ids: - factor = self.read(cr, uid, ids[0], ['factor_base'])['factor_base'] - if company_id: - company_currency = company_obj.read(cr, uid, [company_id], ['currency_id'])[0]['currency_id'][0] - if currency_id and company_currency: - base = cur_obj.compute(cr, uid, currency_id, company_currency, base*factor, context={'date': date_invoice or time.strftime('%Y-%m-%d')}, round=False) - return {'value': {'base_amount':base}} - - def amount_change(self, cr, uid, ids, amount, currency_id=False, company_id=False, date_invoice=False): - cur_obj = self.pool.get('res.currency') - company_obj = self.pool.get('res.company') - company_currency = False - factor = 1 - if ids: - factor = self.read(cr, uid, ids[0], ['factor_tax'])['factor_tax'] - if company_id: - company_currency = company_obj.read(cr, uid, [company_id], ['currency_id'])[0]['currency_id'][0] - if currency_id and company_currency: - amount = cur_obj.compute(cr, uid, currency_id, company_currency, amount*factor, context={'date': date_invoice or time.strftime('%Y-%m-%d')}, round=False) + @api.multi + def amount_change(self, amount, currency_id=False, company_id=False, date_invoice=False): + factor = self.factor_tax if self else 1 + company = self.env['res.company'].browse(company_id) + if currency_id and company.currency_id: + currency = self.env['res.currency'].browse(currency_id) + currency = currency.with_context(date=date_invoice or fields.Date.today()) + amount = currency.compute(amount * factor, company.currency_id, round=False) return {'value': {'tax_amount': amount}} - _order = 'sequence' - _defaults = { - 'manual': 1, - 'base_amount': 0.0, - 'tax_amount': 0.0, - } - def compute(self, cr, uid, invoice_id, context=None): + @api.v8 + def compute(self, invoice): tax_grouped = {} - tax_obj = self.pool.get('account.tax') - cur_obj = self.pool.get('res.currency') - inv = self.pool.get('account.invoice').browse(cr, uid, invoice_id, context=context) - cur = inv.currency_id - company_currency = self.pool['res.company'].browse(cr, uid, inv.company_id.id).currency_id.id - for line in inv.invoice_line: - for tax in tax_obj.compute_all(cr, uid, line.invoice_line_tax_id, (line.price_unit* (1-(line.discount or 0.0)/100.0)), line.quantity, line.product_id, inv.partner_id)['taxes']: - val={} - val['invoice_id'] = inv.id - val['name'] = tax['name'] - val['amount'] = tax['amount'] - val['manual'] = False - val['sequence'] = tax['sequence'] - val['base'] = cur_obj.round(cr, uid, cur, tax['price_unit'] * line['quantity']) - - if inv.type in ('out_invoice','in_invoice'): + currency = invoice.currency_id.with_context(date=invoice.date_invoice or fields.Date.today()) + company_currency = invoice.company_id.currency_id + for line in invoice.invoice_line: + taxes = line.invoice_line_tax_id.compute_all( + (line.price_unit * (1 - (line.discount or 0.0) / 100.0)), + line.quantity, line.product_id, invoice.partner_id)['taxes'] + for tax in taxes: + val = { + 'invoice_id': invoice.id, + 'name': tax['name'], + 'amount': tax['amount'], + 'manual': False, + 'sequence': tax['sequence'], + 'base': currency.round(tax['price_unit'] * line['quantity']), + } + if invoice.type in ('out_invoice','in_invoice'): val['base_code_id'] = tax['base_code_id'] val['tax_code_id'] = tax['tax_code_id'] - val['base_amount'] = cur_obj.compute(cr, uid, inv.currency_id.id, company_currency, val['base'] * tax['base_sign'], context={'date': inv.date_invoice or time.strftime('%Y-%m-%d')}, round=False) - val['tax_amount'] = cur_obj.compute(cr, uid, inv.currency_id.id, company_currency, val['amount'] * tax['tax_sign'], context={'date': inv.date_invoice or time.strftime('%Y-%m-%d')}, round=False) + val['base_amount'] = currency.compute(val['base'] * tax['base_sign'], company_currency, round=False) + val['tax_amount'] = currency.compute(val['amount'] * tax['tax_sign'], company_currency, round=False) val['account_id'] = tax['account_collected_id'] or line.account_id.id val['account_analytic_id'] = tax['account_analytic_collected_id'] else: val['base_code_id'] = tax['ref_base_code_id'] val['tax_code_id'] = tax['ref_tax_code_id'] - val['base_amount'] = cur_obj.compute(cr, uid, inv.currency_id.id, company_currency, val['base'] * tax['ref_base_sign'], context={'date': inv.date_invoice or time.strftime('%Y-%m-%d')}, round=False) - val['tax_amount'] = cur_obj.compute(cr, uid, inv.currency_id.id, company_currency, val['amount'] * tax['ref_tax_sign'], context={'date': inv.date_invoice or time.strftime('%Y-%m-%d')}, round=False) + val['base_amount'] = currency.compute(val['base'] * tax['ref_base_sign'], company_currency, round=False) + val['tax_amount'] = currency.compute(val['amount'] * tax['ref_tax_sign'], company_currency, round=False) val['account_id'] = tax['account_paid_id'] or line.account_id.id val['account_analytic_id'] = tax['account_analytic_paid_id'] @@ -1739,46 +1553,55 @@ class account_invoice_tax(osv.osv): if not key in tax_grouped: tax_grouped[key] = val else: - tax_grouped[key]['amount'] += val['amount'] tax_grouped[key]['base'] += val['base'] + tax_grouped[key]['amount'] += val['amount'] tax_grouped[key]['base_amount'] += val['base_amount'] tax_grouped[key]['tax_amount'] += val['tax_amount'] for t in tax_grouped.values(): - t['base'] = cur_obj.round(cr, uid, cur, t['base']) - t['amount'] = cur_obj.round(cr, uid, cur, t['amount']) - t['base_amount'] = cur_obj.round(cr, uid, cur, t['base_amount']) - t['tax_amount'] = cur_obj.round(cr, uid, cur, t['tax_amount']) + t['base'] = currency.round(t['base']) + t['amount'] = currency.round(t['amount']) + t['base_amount'] = currency.round(t['base_amount']) + t['tax_amount'] = currency.round(t['tax_amount']) + return tax_grouped - def move_line_get(self, cr, uid, invoice_id): + @api.v7 + def compute(self, cr, uid, invoice_id, context=None): + recs = self.browse(cr, uid, [], context) + invoice = recs.env['account.invoice'].browse(invoice_id) + return recs.compute(invoice) + + @api.model + def move_line_get(self, invoice_id): res = [] - cr.execute('SELECT * FROM account_invoice_tax WHERE invoice_id=%s', (invoice_id,)) - for t in cr.dictfetchall(): - if not t['amount'] \ - and not t['tax_code_id'] \ - and not t['tax_amount']: + self._cr.execute( + 'SELECT * FROM account_invoice_tax WHERE invoice_id = %s', + (invoice_id,) + ) + for row in self._cr.dictfetchall(): + if not (row['amount'] or row['tax_code_id'] or row['tax_amount']): continue res.append({ - 'type':'tax', - 'name':t['name'], - 'price_unit': t['amount'], + 'type': 'tax', + 'name': row['name'], + 'price_unit': row['amount'], 'quantity': 1, - 'price': t['amount'] or 0.0, - 'account_id': t['account_id'], - 'tax_code_id': t['tax_code_id'], - 'tax_amount': t['tax_amount'], - 'account_analytic_id': t['account_analytic_id'], + 'price': row['amount'] or 0.0, + 'account_id': row['account_id'], + 'tax_code_id': row['tax_code_id'], + 'tax_amount': row['tax_amount'], + 'account_analytic_id': row['account_analytic_id'], }) return res -class res_partner(osv.osv): - """ Inherits partner and adds invoice information in the partner form """ +class res_partner(models.Model): + # Inherits partner and adds invoice information in the partner form _inherit = 'res.partner' - _columns = { - 'invoice_ids': fields.one2many('account.invoice', 'partner_id', 'Invoices', readonly=True), - } + + invoice_ids = fields.One2many('account.invoice', 'partner_id', string='Invoices', + readonly=True) def _find_accounting_partner(self, partner): ''' @@ -1786,21 +1609,18 @@ class res_partner(osv.osv): ''' return partner.commercial_partner_id - def copy(self, cr, uid, id, default=None, context=None): - default = default or {} - default.update({'invoice_ids' : []}) - return super(res_partner, self).copy(cr, uid, id, default, context) - - -class mail_compose_message(osv.Model): +class mail_compose_message(models.Model): _inherit = 'mail.compose.message' - def send_mail(self, cr, uid, ids, context=None): - context = context or {} - if context.get('default_model') == 'account.invoice' and context.get('default_res_id') and context.get('mark_invoice_as_sent'): - context = dict(context, mail_post_autofollow=True) - self.pool.get('account.invoice').write(cr, uid, [context['default_res_id']], {'sent': True}, context=context) - self.pool.get('account.invoice').message_post(cr, uid, [context['default_res_id']], body=_("Invoice sent"), context=context) - return super(mail_compose_message, self).send_mail(cr, uid, ids, context=context) + @api.multi + def send_mail(self): + context = self._context + if context.get('default_model') == 'account.invoice' and \ + context.get('default_res_id') and context.get('mark_invoice_as_sent'): + invoice = self.env['account.invoice'].browse(context['default_res_id']) + invoice = invoice.with_context(mail_post_autofollow=True) + self.write({'sent': True}) + self.message_post(body=_("Invoice sent")) + return super(mail_compose_message, self).send_mail() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/account/account_invoice_view.xml b/addons/account/account_invoice_view.xml index ef4edef7e14..64159dab18d 100644 --- a/addons/account/account_invoice_view.xml +++ b/addons/account/account_invoice_view.xml @@ -183,7 +183,7 @@ + on_change="onchange_journal_id(journal_id)" options="{'no_create': True}"/> @@ -258,7 +258,7 @@ - + @@ -329,7 +329,7 @@ + on_change="onchange_journal_id(journal_id)" options="{'no_create': True}"/> @@ -393,7 +393,7 @@ - + 0) and journal: x = journal_obj.browse(cr, uid, journal).default_credit_account_id if x: acc = x + context = dict(context) context.update({ 'date': date, 'res.currency.compute.account': acc, @@ -1006,12 +1000,8 @@ class account_move_line(osv.osv): period_pool = self.pool.get('account.period') pids = period_pool.find(cr, user, date, context=context) if pids: - res.update({ - 'period_id':pids[0] - }) - context.update({ - 'period_id':pids[0] - }) + res.update({'period_id':pids[0]}) + context = dict(context, period_id=pids[0]) return { 'value':res, 'context':context, @@ -1158,15 +1148,14 @@ class account_move_line(osv.osv): move_obj = self.pool.get('account.move') cur_obj = self.pool.get('res.currency') journal_obj = self.pool.get('account.journal') - if context is None: - context = {} + context = dict(context or {}) if vals.get('move_id', False): move = self.pool.get('account.move').browse(cr, uid, vals['move_id'], context=context) if move.company_id: vals['company_id'] = move.company_id.id if move.date and not vals.get('date'): vals['date'] = move.date - if ('account_id' in vals) and not account_obj.read(cr, uid, vals['account_id'], ['active'])['active']: + if ('account_id' in vals) and not account_obj.read(cr, uid, [vals['account_id']], ['active'])[0]['active']: raise osv.except_osv(_('Bad Account!'), _('You cannot use an inactive account.')) if 'journal_id' in vals and vals['journal_id']: context['journal_id'] = vals['journal_id'] diff --git a/addons/account/account_view.xml b/addons/account/account_view.xml index c447576b348..5e80bfdadfe 100644 --- a/addons/account/account_view.xml +++ b/addons/account/account_view.xml @@ -1209,7 +1209,7 @@ - + diff --git a/addons/account/edi/invoice.py b/addons/account/edi/invoice.py index c82d041ef85..ad3a5bdfd58 100644 --- a/addons/account/edi/invoice.py +++ b/addons/account/edi/invoice.py @@ -208,7 +208,7 @@ class account_invoice(osv.osv, EDIMixin): edi_document.pop('partner_ref', None) # journal_id: should be selected based on type: simply put the 'type' in the context when calling create(), will be selected correctly - context.update(type=invoice_type) + context = dict(context, type=invoice_type) # for invoice lines, the account_id value should be taken from the product's default, i.e. from the default category, as it will not be provided. for edi_invoice_line in edi_document['invoice_line']: diff --git a/addons/account/ir_sequence.py b/addons/account/ir_sequence.py index d3615a847b7..19d7ee0cf87 100644 --- a/addons/account/ir_sequence.py +++ b/addons/account/ir_sequence.py @@ -18,7 +18,7 @@ # along with this program. If not, see . # ############################################################################## - +from openerp import api from openerp.osv import fields, osv class ir_sequence_fiscalyear(osv.osv): @@ -43,9 +43,10 @@ class ir_sequence(osv.osv): _inherit = 'ir.sequence' _columns = { 'fiscal_ids': fields.one2many('account.sequence.fiscalyear', - 'sequence_main_id', 'Sequences') + 'sequence_main_id', 'Sequences', copy=True) } + @api.cr_uid_ids_context def _next(self, cr, uid, seq_ids, context=None): if context is None: context = {} diff --git a/addons/account/partner.py b/addons/account/partner.py index 70f5280b861..afbd9114c10 100644 --- a/addons/account/partner.py +++ b/addons/account/partner.py @@ -23,6 +23,7 @@ from operator import itemgetter import time from openerp.osv import fields, osv +from openerp import api class account_fiscal_position(osv.osv): _name = 'account.fiscal.position' @@ -33,8 +34,8 @@ class account_fiscal_position(osv.osv): 'name': fields.char('Fiscal Position', required=True), 'active': fields.boolean('Active', help="By unchecking the active field, you may hide a fiscal position without deleting it."), 'company_id': fields.many2one('res.company', 'Company'), - 'account_ids': fields.one2many('account.fiscal.position.account', 'position_id', 'Account Mapping'), - 'tax_ids': fields.one2many('account.fiscal.position.tax', 'position_id', 'Tax Mapping'), + 'account_ids': fields.one2many('account.fiscal.position.account', 'position_id', 'Account Mapping', copy=True), + 'tax_ids': fields.one2many('account.fiscal.position.tax', 'position_id', 'Tax Mapping', copy=True), 'note': fields.text('Notes'), 'auto_apply': fields.boolean('Automatic', help="Apply automatically this fiscal position."), 'vat_required': fields.boolean('VAT required', help="Apply only if partner has a VAT number."), @@ -46,6 +47,7 @@ class account_fiscal_position(osv.osv): 'active': True, } + @api.v7 def map_tax(self, cr, uid, fposition_id, taxes, context=None): if not taxes: return [] @@ -63,6 +65,20 @@ class account_fiscal_position(osv.osv): result.add(t.id) return list(result) + @api.v8 + def map_tax(self, taxes): + result = taxes.browse() + for tax in taxes: + found = False + for t in self.tax_ids: + if t.tax_src_id == tax: + result |= t.tax_dest_id + found = True + if not found: + result |= tax + return result + + @api.v7 def map_account(self, cr, uid, fposition_id, account_id, context=None): if not fposition_id: return account_id @@ -72,6 +88,13 @@ class account_fiscal_position(osv.osv): break return account_id + @api.v8 + def map_account(self, account): + for pos in self.account_ids: + if pos.account_src_id == account: + return pos.account_dest_id + return account + def get_fiscal_position(self, cr, uid, company_id, partner_id, delivery_id=None, context=None): if not partner_id: return False @@ -279,7 +302,14 @@ class res_partner(osv.osv): help="This payment term will be used instead of the default one for purchase orders and supplier invoices"), 'ref_companies': fields.one2many('res.company', 'partner_id', 'Companies that refers to partner'), - 'last_reconciliation_date': fields.datetime('Latest Full Reconciliation Date', help='Date on which the partner accounting entries were fully reconciled last time. It differs from the last date where a reconciliation has been made for this partner, as here we depict the fact that nothing more was to be reconciled at this date. This can be achieved in 2 different ways: either the last unreconciled debit/credit entry of this partner was reconciled, either the user pressed the button "Nothing more to reconcile" during the manual reconciliation process.') + 'last_reconciliation_date': fields.datetime( + 'Latest Full Reconciliation Date', copy=False, + help='Date on which the partner accounting entries were fully reconciled last time. ' + 'It differs from the last date where a reconciliation has been made for this partner, ' + 'as here we depict the fact that nothing more was to be reconciled at this date. ' + 'This can be achieved in 2 different ways: either the last unreconciled debit/credit ' + 'entry of this partner was reconciled, either the user pressed the button ' + '"Nothing more to reconcile" during the manual reconciliation process.') } def _commercial_fields(self, cr, uid, context=None): diff --git a/addons/account/report/account_journal.py b/addons/account/report/account_journal.py index 13dfd538f4a..1854e6601ab 100644 --- a/addons/account/report/account_journal.py +++ b/addons/account/report/account_journal.py @@ -156,6 +156,7 @@ class journal_print(report_sxw.rml_parse, common_report_header): journal_id = [journal_id] obj_mline = self.pool.get('account.move.line') self.cr.execute('update account_journal_period set state=%s where journal_id IN %s and period_id=%s and state=%s', ('printed', self.journal_ids, period_id, 'draft')) + self.pool.get('account.journal.period').invalidate_cache(self.cr, self.uid, ['state'], context=self.context) move_state = ['draft','posted'] if self.target_move == 'posted': diff --git a/addons/account/report/account_report.py b/addons/account/report/account_report.py index fbe23d2c6dc..8b650719151 100644 --- a/addons/account/report/account_report.py +++ b/addons/account/report/account_report.py @@ -20,7 +20,7 @@ ############################################################################## import time -from datetime import datetime +import datetime from dateutil.relativedelta import relativedelta from openerp import tools @@ -91,7 +91,7 @@ class report_aged_receivable(osv.osv): """ if context is None:context = {} if not self.called: - self.init(cr, user) + self._init(cr, user) self.called = True # To make sure that init doesn't get called multiple times res = super(report_aged_receivable, self).fields_view_get(cr, user, view_id, view_type, context, toolbar=toolbar, submenu=submenu) @@ -116,29 +116,37 @@ class report_aged_receivable(osv.osv): 'balance': fields.function(_calc_bal, string='Balance', readonly=True), } - def init(self, cr, uid=1): + def init(self, cr): + return self._init(cr, 1) + + def _init(self, cr, uid): """ This view will be used in dashboard The reason writing this code here is, we need to check date range from today to first date of fiscal year. """ pool_obj_fy = self.pool['account.fiscalyear'] - today = time.strftime('%Y-%m-%d') + current_date = datetime.date.today() fy_id = pool_obj_fy.find(cr, uid, exception=False) - LIST_RANGES = [] + names = [] + + def add(names, start_on, stop_on): + names.append(start_on.strftime("%Y-%m-%d") + ' to ' + stop_on.strftime('%Y-%m-%d')) + return names + if fy_id: - fy_start_date = pool_obj_fy.read(cr, uid, fy_id, ['date_start'])['date_start'] - fy_start_date = datetime.strptime(fy_start_date, '%Y-%m-%d') - last_month_date = datetime.strptime(today, '%Y-%m-%d') - relativedelta(months=1) + fiscal_year = pool_obj_fy.browse(cr, uid, fy_id) + fy_start_date = datetime.datetime.strptime(fiscal_year.date_start, '%Y-%m-%d').date() + last_month_date = current_date - relativedelta(months=1) while (last_month_date > fy_start_date): - LIST_RANGES.append(today + " to " + last_month_date.strftime('%Y-%m-%d')) - today = (last_month_date- relativedelta(days=1)).strftime('%Y-%m-%d') - last_month_date = datetime.strptime(today, '%Y-%m-%d') - relativedelta(months=1) + add(names, current_date, last_month_date) + current_date = last_month_date - relativedelta(days=1) + last_month_date = current_date - relativedelta(months=1) - LIST_RANGES.append(today +" to " + fy_start_date.strftime('%Y-%m-%d')) + add(names, current_date, fy_start_date) cr.execute('delete from temp_range') - for range in LIST_RANGES: - self.pool['temp.range'].create(cr, uid, {'name':range}) + for name in names: + self.pool['temp.range'].create(cr, uid, {'name':name}) cr.execute(""" create or replace view report_aged_receivable as ( diff --git a/addons/account/wizard/account_automatic_reconcile.py b/addons/account/wizard/account_automatic_reconcile.py index 2ad701a9bf5..3ddad1c050c 100644 --- a/addons/account/wizard/account_automatic_reconcile.py +++ b/addons/account/wizard/account_automatic_reconcile.py @@ -239,7 +239,7 @@ class account_automatic_reconcile(osv.osv_memory): (account_id.id,)) additional_unrec = cr.fetchone()[0] unreconciled = unreconciled + additional_unrec - context.update({'reconciled': reconciled, 'unreconciled': unreconciled}) + context = dict(context, reconciled=reconciled, unreconciled=unreconciled) model_data_ids = obj_model.search(cr,uid,[('model','=','ir.ui.view'),('name','=','account_automatic_reconcile_view1')]) resource_id = obj_model.read(cr, uid, model_data_ids, fields=['res_id'])[0]['res_id'] return { diff --git a/addons/account/wizard/account_chart.py b/addons/account/wizard/account_chart.py index de652a947f0..73be9040d22 100644 --- a/addons/account/wizard/account_chart.py +++ b/addons/account/wizard/account_chart.py @@ -85,7 +85,7 @@ class account_chart(osv.osv_memory): fy_obj = self.pool.get('account.fiscalyear') if context is None: context = {} - data = self.read(cr, uid, ids, [], context=context)[0] + data = self.read(cr, uid, ids, context=context)[0] result = mod_obj.get_object_reference(cr, uid, 'account', 'action_account_tree') id = result and result[1] or False result = act_obj.read(cr, uid, [id], context=context)[0] diff --git a/addons/account/wizard/account_fiscalyear_close.py b/addons/account/wizard/account_fiscalyear_close.py index 3e3bbb5fd93..9ad646df32c 100644 --- a/addons/account/wizard/account_fiscalyear_close.py +++ b/addons/account/wizard/account_fiscalyear_close.py @@ -62,6 +62,7 @@ class account_fiscalyear_close(osv.osv_memory): raise osv.except_osv(_('Warning!'), _('The entries to reconcile should belong to the same company.')) r_id = self.pool.get('account.move.reconcile').create(cr, uid, {'type': 'auto', 'opening_reconciliation': True}) cr.execute('update account_move_line set reconcile_id = %s where id in %s',(r_id, tuple(ids),)) + obj_acc_move_line.invalidate_cache(cr, uid, ['reconcile_id'], ids, context=context) return r_id obj_acc_period = self.pool.get('account.period') @@ -175,6 +176,7 @@ class account_fiscalyear_close(osv.osv_memory): AND b.reconcile_id IN (SELECT DISTINCT(reconcile_id) FROM account_move_line a WHERE a.period_id IN ('''+fy2_period_set+''')))''', (new_journal.id, period.id, period.date_start, move_id, tuple(account_ids),)) + self.invalidate_cache(cr, uid, context=context) #2. report of the accounts with defferal method == 'detail' cr.execute(''' @@ -203,7 +205,7 @@ class account_fiscalyear_close(osv.osv_memory): WHERE account_id IN %s AND ''' + query_line + ''') ''', (new_journal.id, period.id, period.date_start, move_id, tuple(account_ids),)) - + self.invalidate_cache(cr, uid, context=context) #3. report of the accounts with defferal method == 'balance' cr.execute(''' @@ -243,6 +245,7 @@ class account_fiscalyear_close(osv.osv_memory): 'draft') if query_2nd_part: cr.execute(query_1st_part + query_2nd_part, tuple(query_2nd_part_args)) + self.invalidate_cache(cr, uid, context=context) #validate and centralize the opening move obj_acc_move.validate(cr, uid, [move_id], context=context) @@ -267,6 +270,7 @@ class account_fiscalyear_close(osv.osv_memory): cr.execute('UPDATE account_fiscalyear ' \ 'SET end_journal_period_id = %s ' \ 'WHERE id = %s', (ids[0], old_fyear.id)) + obj_acc_fiscalyear.invalidate_cache(cr, uid, ['end_journal_period_id'], [old_fyear.id], context=context) return {'type': 'ir.actions.act_window_close'} diff --git a/addons/account/wizard/account_fiscalyear_close_state.py b/addons/account/wizard/account_fiscalyear_close_state.py index ed84ab65184..de2aa2134ee 100644 --- a/addons/account/wizard/account_fiscalyear_close_state.py +++ b/addons/account/wizard/account_fiscalyear_close_state.py @@ -41,6 +41,10 @@ class account_fiscalyear_close_state(osv.osv_memory): @param ids: List of Account fiscalyear close state’s IDs """ + journal_period_obj = self.pool.get('account.journal.period') + period_obj = self.pool.get('account.period') + fiscalyear_obj = self.pool.get('account.fiscalyear') + for data in self.read(cr, uid, ids, context=context): fy_id = data['fy_id'][0] @@ -53,6 +57,7 @@ class account_fiscalyear_close_state(osv.osv_memory): 'WHERE fiscalyear_id = %s', ('done', fy_id)) cr.execute('UPDATE account_fiscalyear ' \ 'SET state = %s WHERE id = %s', ('done', fy_id)) + self.invalidate_cache(cr, uid, context=context) return {'type': 'ir.actions.act_window_close'} diff --git a/addons/account/wizard/account_invoice_refund.py b/addons/account/wizard/account_invoice_refund.py index d2d408dce68..0d6d7391ccf 100644 --- a/addons/account/wizard/account_invoice_refund.py +++ b/addons/account/wizard/account_invoice_refund.py @@ -68,10 +68,10 @@ class account_invoice_refund(osv.osv_memory): } def fields_view_get(self, cr, uid, view_id=None, view_type=False, context=None, toolbar=False, submenu=False): - if context is None:context = {} journal_obj = self.pool.get('account.journal') user_obj = self.pool.get('res.users') # remove the entry with key 'form_view_ref', otherwise fields_view_get crashes + context = dict(context or {}) context.pop('form_view_ref', None) res = super(account_invoice_refund,self).fields_view_get(cr, uid, view_id=view_id, view_type=view_type, context=context, toolbar=toolbar, submenu=submenu) type = context.get('type', 'out_invoice') @@ -168,7 +168,7 @@ class account_invoice_refund(osv.osv_memory): to_reconcile_ids.setdefault(line.account_id.id, []).append(line.id) if line.reconcile_id: line.reconcile_id.unlink() - inv_obj.signal_invoice_open(cr, uid, [refund.id]) + refund.signal_workflow('invoice_open') refund = inv_obj.browse(cr, uid, refund_id[0], context=context) for tmpline in refund.move_id.line_id: if tmpline.account_id.id == inv.account_id.id: @@ -212,13 +212,15 @@ class account_invoice_refund(osv.osv_memory): if 'value' in data and data['value']: inv_obj.write(cr, uid, [inv_id], data['value']) created_inv.append(inv_id) + xml_id = (inv.type == 'out_refund') and 'action_invoice_tree1' or \ (inv.type == 'in_refund') and 'action_invoice_tree2' or \ (inv.type == 'out_invoice') and 'action_invoice_tree3' or \ (inv.type == 'in_invoice') and 'action_invoice_tree4' result = mod_obj.get_object_reference(cr, uid, 'account', xml_id) id = result and result[1] or False - result = act_obj.read(cr, uid, id, context=context) + + result = act_obj.read(cr, uid, [id], context=context)[0] invoice_domain = eval(result['domain']) invoice_domain.append(('id', 'in', created_inv)) result['domain'] = invoice_domain diff --git a/addons/account/wizard/account_invoice_state.py b/addons/account/wizard/account_invoice_state.py index 1aed2c6c477..75b4802bd58 100644 --- a/addons/account/wizard/account_invoice_state.py +++ b/addons/account/wizard/account_invoice_state.py @@ -33,12 +33,13 @@ class account_invoice_confirm(osv.osv_memory): def invoice_confirm(self, cr, uid, ids, context=None): if context is None: context = {} - account_invoice_obj = self.pool['account.invoice'] - data_inv = account_invoice_obj.read(cr, uid, context['active_ids'], ['state'], context=context) - for record in data_inv: - if record['state'] not in ('draft','proforma','proforma2'): + active_ids = context.get('active_ids', []) or [] + + proxy = self.pool['account.invoice'] + for record in proxy.browse(cr, uid, active_ids, context=context): + if record.state not in ('draft', 'proforma', 'proforma2'): raise osv.except_osv(_('Warning!'), _("Selected invoice(s) cannot be confirmed as they are not in 'Draft' or 'Pro-Forma' state.")) - account_invoice_obj.signal_invoice_open(cr, uid, [ record['id'] ]) + record.signal_workflow('invoice_open') return {'type': 'ir.actions.act_window_close'} @@ -55,12 +56,13 @@ class account_invoice_cancel(osv.osv_memory): def invoice_cancel(self, cr, uid, ids, context=None): if context is None: context = {} - account_invoice_obj = self.pool['account.invoice'] - data_inv = account_invoice_obj.read(cr, uid, context['active_ids'], ['state'], context=context) - for record in data_inv: - if record['state'] in ('cancel','paid'): + proxy = self.pool['account.invoice'] + active_ids = context.get('active_ids', []) or [] + + for record in proxy.browse(cr, uid, active_ids, context=context): + if record.state in ('cancel','paid'): raise osv.except_osv(_('Warning!'), _("Selected invoice(s) cannot be cancelled as they are already in 'Cancelled' or 'Done' state.")) - account_invoice_obj.signal_invoice_cancel(cr , uid, [record['id']]) + record.signal_workflow('invoice_cancel') return {'type': 'ir.actions.act_window_close'} diff --git a/addons/account/wizard/account_open_closed_fiscalyear.py b/addons/account/wizard/account_open_closed_fiscalyear.py index f4e90ae9f2f..c142dcc3e21 100644 --- a/addons/account/wizard/account_open_closed_fiscalyear.py +++ b/addons/account/wizard/account_open_closed_fiscalyear.py @@ -41,6 +41,7 @@ class account_open_closed_fiscalyear(osv.osv_memory): ids_move = move_obj.search(cr, uid, [('journal_id','=',period_journal.journal_id.id),('period_id','=',period_journal.period_id.id)]) if ids_move: cr.execute('delete from account_move where id IN %s', (tuple(ids_move),)) + self.invalidate_cache(cr, uid, context=context) return {'type': 'ir.actions.act_window_close'} diff --git a/addons/account/wizard/account_period_close.py b/addons/account/wizard/account_period_close.py index a50861c65ef..ada52c389ef 100644 --- a/addons/account/wizard/account_period_close.py +++ b/addons/account/wizard/account_period_close.py @@ -39,6 +39,7 @@ class account_period_close(osv.osv_memory): @param uid: the current user’s ID for security checks, @param ids: account period close’s ID or list of IDs """ + journal_period_pool = self.pool.get('account.journal.period') period_pool = self.pool.get('account.period') account_move_obj = self.pool.get('account.move') @@ -52,6 +53,7 @@ class account_period_close(osv.osv_memory): cr.execute('update account_journal_period set state=%s where period_id=%s', (mode, id)) cr.execute('update account_period set state=%s where id=%s', (mode, id)) + self.invalidate_cache(cr, uid, context=context) return {'type': 'ir.actions.act_window_close'} diff --git a/addons/account/wizard/account_reconcile.py b/addons/account/wizard/account_reconcile.py index 804003c975a..656661a34b9 100644 --- a/addons/account/wizard/account_reconcile.py +++ b/addons/account/wizard/account_reconcile.py @@ -140,6 +140,7 @@ class account_move_line_reconcile_writeoff(osv.osv_memory): return {'type': 'ir.actions.act_window_close'} def trans_rec_reconcile(self, cr, uid, ids, context=None): + context = dict(context or {}) account_move_line_obj = self.pool.get('account.move.line') period_obj = self.pool.get('account.period') if context is None: diff --git a/addons/account/wizard/account_state_open.py b/addons/account/wizard/account_state_open.py index 1950a139983..e622a1ea55b 100644 --- a/addons/account/wizard/account_state_open.py +++ b/addons/account/wizard/account_state_open.py @@ -27,14 +27,16 @@ class account_state_open(osv.osv_memory): _description = 'Account State Open' def change_inv_state(self, cr, uid, ids, context=None): - obj_invoice = self.pool.get('account.invoice') + proxy = self.pool.get('account.invoice') if context is None: context = {} - if 'active_ids' in context: - data_inv = obj_invoice.browse(cr, uid, context['active_ids'][0], context=context) - if data_inv.reconciled: + + active_ids = context.get('active_ids') + if isinstance(active_ids, list): + invoice = proxy.browse(cr, uid, active_ids[0], context=context) + if invoice.reconciled: raise osv.except_osv(_('Warning!'), _('Invoice is already reconciled.')) - obj_invoice.signal_open_test(cr, uid, context['active_ids'][0]) + invoice.signal_workflow('open_test') return {'type': 'ir.actions.act_window_close'} diff --git a/addons/account/wizard/account_statement_from_invoice.py b/addons/account/wizard/account_statement_from_invoice.py index 285b8e336b3..de2d44499fd 100644 --- a/addons/account/wizard/account_statement_from_invoice.py +++ b/addons/account/wizard/account_statement_from_invoice.py @@ -34,8 +34,7 @@ class account_statement_from_invoice_lines(osv.osv_memory): } def populate_statement(self, cr, uid, ids, context=None): - if context is None: - context = {} + context = dict(context or {}) statement_id = context.get('statement_id', False) if not statement_id: return {'type': 'ir.actions.act_window_close'} diff --git a/addons/account/wizard/account_use_model.py b/addons/account/wizard/account_use_model.py index 06f02719065..be9944f3db7 100644 --- a/addons/account/wizard/account_use_model.py +++ b/addons/account/wizard/account_use_model.py @@ -58,7 +58,7 @@ class account_use_model(osv.osv_memory): model_ids = context['active_ids'] move_ids = account_model_obj.generate(cr, uid, model_ids, context=context) - context.update({'move_ids':move_ids}) + context = dict(context, move_ids=move_ids) model_data_ids = mod_obj.search(cr, uid,[('model','=','ir.ui.view'),('name','=','view_move_form')], context=context) resource_id = mod_obj.read(cr, uid, model_data_ids, fields=['res_id'], context=context)[0]['res_id'] return { diff --git a/addons/account_analytic_analysis/account_analytic_analysis.py b/addons/account_analytic_analysis/account_analytic_analysis.py index 9550f77bcab..3b5819968a3 100644 --- a/addons/account_analytic_analysis/account_analytic_analysis.py +++ b/addons/account_analytic_analysis/account_analytic_analysis.py @@ -24,7 +24,6 @@ import logging import time from openerp.osv import osv, fields -from openerp.osv.orm import intersect, except_orm import openerp.tools from openerp.tools.translate import _ @@ -518,7 +517,7 @@ class account_analytic_account(osv.osv): 'invoiced_total' : fields.function(_sum_of_fields, type="float",multi="sum_of_all", string="Total Invoiced"), 'remaining_total' : fields.function(_sum_of_fields, type="float",multi="sum_of_all", string="Total Remaining", help="Expectation of remaining income for this contract. Computed as the sum of remaining subtotals which, in turn, are computed as the maximum between '(Estimation - Invoiced)' and 'To Invoice' amounts"), 'toinvoice_total' : fields.function(_sum_of_fields, type="float",multi="sum_of_all", string="Total to Invoice", help=" Sum of everything that could be invoiced for this contract."), - 'recurring_invoice_line_ids': fields.one2many('account.analytic.invoice.line', 'analytic_account_id', 'Invoice Lines'), + 'recurring_invoice_line_ids': fields.one2many('account.analytic.invoice.line', 'analytic_account_id', 'Invoice Lines', copy=True), 'recurring_invoices' : fields.boolean('Generate recurring invoices automatically'), 'recurring_rule_type': fields.selection([ ('daily', 'Day(s)'), @@ -595,8 +594,7 @@ class account_analytic_account(osv.osv): return value def cron_account_analytic_account(self, cr, uid, context=None): - if context is None: - context = {} + context = dict(context or {}) remind = {} def fill_remind(key, domain, write_pending=False): @@ -612,7 +610,7 @@ class account_analytic_account(osv.osv): accounts = self.browse(cr, uid, accounts_ids, context=context) for account in accounts: if write_pending: - account.write({'state' : 'pending'}, context=context) + account.write({'state' : 'pending'}) remind_user = remind.setdefault(account.manager_id.id, {}) remind_type = remind_user.setdefault(key, {}) remind_partner = remind_type.setdefault(account.partner_id, []).append(account) diff --git a/addons/account_analytic_plans/account_analytic_plans.py b/addons/account_analytic_plans/account_analytic_plans.py index a8cb69b0de0..510632321d5 100644 --- a/addons/account_analytic_plans/account_analytic_plans.py +++ b/addons/account_analytic_plans/account_analytic_plans.py @@ -43,8 +43,15 @@ class one2many_mod2(fields.one2many): ids2 = obj.pool[self._obj].search(cr, user, [(self._fields_id,'in',ids),('analytic_account_id','child_of',[acc_id])], limit=self._limit) if ids2 is None: ids2 = obj.pool[self._obj].search(cr, user, [(self._fields_id,'in',ids)], limit=self._limit) - for r in obj.pool[self._obj]._read_flat(cr, user, ids2, [self._fields_id], context=context, load='_classic_write'): - res[r[self._fields_id]].append( r['id'] ) + + for r in obj.pool[self._obj].read(cr, user, ids2, [self._fields_id], context=context, load='_classic_write'): + key = r[self._fields_id] + if isinstance(key, tuple): + # Read return a tuple in the case where the field is a many2one + # but we want to get the id of this field. + key = key[0] + + res[key].append( r['id'] ) return res class account_analytic_line(osv.osv): @@ -71,7 +78,7 @@ class account_analytic_plan(osv.osv): _description = "Analytic Plan" _columns = { 'name': fields.char('Analytic Plan', required=True, select=True), - 'plan_ids': fields.one2many('account.analytic.plan.line', 'plan_id', 'Analytic Plans'), + 'plan_ids': fields.one2many('account.analytic.plan.line', 'plan_id', 'Analytic Plans', copy=True), } @@ -100,7 +107,7 @@ class account_analytic_plan_instance(osv.osv): 'name': fields.char('Analytic Distribution'), 'code': fields.char('Distribution Code', size=16), 'journal_id': fields.many2one('account.analytic.journal', 'Analytic Journal' ), - 'account_ids': fields.one2many('account.analytic.plan.instance.line', 'plan_id', 'Account Id'), + 'account_ids': fields.one2many('account.analytic.plan.instance.line', 'plan_id', 'Account Id', copy=True), 'account1_ids': one2many_mod2('account.analytic.plan.instance.line', 'plan_id', 'Account1 Id'), 'account2_ids': one2many_mod2('account.analytic.plan.instance.line', 'plan_id', 'Account2 Id'), 'account3_ids': one2many_mod2('account.analytic.plan.instance.line', 'plan_id', 'Account3 Id'), @@ -124,13 +131,6 @@ class account_analytic_plan_instance(osv.osv): context=context, count=count) return res - def copy(self, cr, uid, id, default=None, context=None): - if not default: - default = {} - default.update({'account1_ids':False, 'account2_ids':False, 'account3_ids':False, - 'account4_ids':False, 'account5_ids':False, 'account6_ids':False}) - return super(account_analytic_plan_instance, self).copy(cr, uid, id, default, context=context) - def _default_journal(self, cr, uid, context=None): if context is None: context = {} @@ -373,8 +373,8 @@ class account_invoice(osv.osv): res['analytics_id'] = x.get('analytics_id', False) return res - def _get_analytic_lines(self, cr, uid, id, context=None): - inv = self.browse(cr, uid, [id])[0] + def _get_analytic_lines(self, cr, uid, ids, context=None): + inv = self.browse(cr, uid, ids)[0] cur_obj = self.pool.get('res.currency') invoice_line_obj = self.pool.get('account.invoice.line') acct_ins_obj = self.pool.get('account.analytic.plan.instance') diff --git a/addons/account_analytic_plans/wizard/account_crossovered_analytic.py b/addons/account_analytic_plans/wizard/account_crossovered_analytic.py index 1bdc91f2995..699c263322e 100644 --- a/addons/account_analytic_plans/wizard/account_crossovered_analytic.py +++ b/addons/account_analytic_plans/wizard/account_crossovered_analytic.py @@ -45,7 +45,7 @@ class account_crossovered_analytic(osv.osv_memory): res = cr.fetchall() acc_ids = [x[0] for x in res] - data = self.read(cr, uid, ids, [], context=context)[0] + data = self.read(cr, uid, ids, context=context)[0] data['ref'] = data['ref'][0] obj_acc = self.pool.get('account.analytic.account').browse(cr, uid, data['ref'], context=context) diff --git a/addons/account_anglo_saxon/test/anglo_saxon.yml b/addons/account_anglo_saxon/test/anglo_saxon.yml index 9c953482f53..c98de7c47db 100644 --- a/addons/account_anglo_saxon/test/anglo_saxon.yml +++ b/addons/account_anglo_saxon/test/anglo_saxon.yml @@ -127,8 +127,8 @@ Reception is ready for process so now done the reception. - !python {model: stock.picking}: | - picking_id = self.pool.get('purchase.order').browse(cr, uid, ref("purchase_order_001")).picking_ids[0] - picking_id.do_transfer(context=context) + picking_id = self.pool.get('purchase.order').browse(cr, uid, ref("purchase_order_001"), context=context).picking_ids[0] + picking_id.do_transfer() - I check the Stock Interim account (Received) is credited successfully. - @@ -144,7 +144,7 @@ - !python {model: purchase.order}: | invoice_ids = [x.id for x in self.browse(cr, uid, ref("purchase_order_001")).invoice_ids] - self.pool.get('account.invoice').signal_invoice_open(cr, uid, invoice_ids) + self.pool.get('account.invoice').signal_workflow(cr, uid, invoice_ids, 'invoice_open') - I check the Stock Interim account (Received) is debited sucessfully when Invoice validated. - @@ -166,7 +166,7 @@ !python {model: purchase.order}: | po = self.browse(cr, uid, ref("purchase_order_001")) for invoice in po.invoice_ids: - self.pool.get('account.invoice').signal_invoice_open(cr, uid, [invoice.id]) + invoice.signal_workflow('invoice_open') - I pay the invoice. - @@ -220,8 +220,8 @@ I process the delivery. - !python {model: stock.picking}: | - picking = self.pool.get('stock.picking').browse(cr, uid, ref("stock_picking_out001")) - picking.do_transfer(context=context) + picking = self.pool.get('stock.picking').browse(cr, uid, ref("stock_picking_out001"), context=context) + picking.do_transfer() - I check Stock Interim account (Delivery) is debited successfully. - @@ -252,7 +252,7 @@ !python {model: stock.picking}: | move_name = self.pool.get('stock.picking').browse(cr, uid, ref('stock_picking_out001')).name account_invoice = self.pool.get('account.invoice').search(cr, uid, [('origin', '=', move_name)]) - self.pool.get('account.invoice').signal_invoice_open(cr, uid, account_invoice) + self.pool.get('account.invoice').signal_workflow(cr, uid, account_invoice, 'invoice_open') - I check Income Account is Credited sucessfully when Invoice validated. - diff --git a/addons/account_anglo_saxon/test/anglo_saxon_avg_fifo.yml b/addons/account_anglo_saxon/test/anglo_saxon_avg_fifo.yml index b9d290c89e3..03442260295 100644 --- a/addons/account_anglo_saxon/test/anglo_saxon_avg_fifo.yml +++ b/addons/account_anglo_saxon/test/anglo_saxon_avg_fifo.yml @@ -135,8 +135,8 @@ Reception is ready for process so now done the reception. - !python {model: stock.picking}: | - picking_id = self.pool.get('purchase.order').browse(cr, uid, ref("purchase_order_001_fifo")).picking_ids[0] - picking_id.do_transfer(context=context) + picking_id = self.pool.get('purchase.order').browse(cr, uid, ref("purchase_order_001_fifo"), context=context).picking_ids[0] + picking_id.do_transfer() - I check the Stock Interim account (Received) is credit successfully. - @@ -154,7 +154,7 @@ invoice_ids = [x.id for x in self.browse(cr, uid, ref("purchase_order_001_fifo")).invoice_ids] line_ids = self.pool.get('account.invoice.line').search(cr, uid, [('invoice_id', 'in', invoice_ids)]) self.pool.get('account.invoice.line').write(cr, uid, line_ids, {'price_unit': 10}) - self.pool.get('account.invoice').signal_invoice_open(cr, uid, invoice_ids) + self.pool.get('account.invoice').signal_workflow(cr, uid, invoice_ids, 'invoice_open') - I check the Stock Interim account (Received) is debited sucessfully when Invoice validated. - @@ -220,8 +220,8 @@ I process the delivery. - !python {model: stock.picking}: | - picking = self.pool.get('stock.picking').browse(cr, uid, ref("stock_picking_out001_fifo")) - picking.do_transfer(context=context) + picking = self.pool.get('stock.picking').browse(cr, uid, ref("stock_picking_out001_fifo"), context=context) + picking.do_transfer() - I check Stock Interim account (Delivery) is debited successfully. - @@ -257,7 +257,7 @@ account_invoice_line = self.pool.get('account.invoice.line').search(cr, uid, [('invoice_id', 'in', account_invoice)]) self.pool.get('account.invoice.line').write(cr, uid, account_invoice_line, {'invoice_line_tax_id': [(6, 0, [])]}) self.pool.get('account.invoice').button_reset_taxes(cr, uid, account_invoice) - self.pool.get('account.invoice').signal_invoice_open(cr, uid, account_invoice) + self.pool.get('account.invoice').signal_workflow(cr, uid, account_invoice, 'invoice_open') - I check Income Account is Credited sucessfully when Invoice validated. - diff --git a/addons/account_asset/account_asset.py b/addons/account_asset/account_asset.py index 36ed9424d5a..be6ffdce3e5 100644 --- a/addons/account_asset/account_asset.py +++ b/addons/account_asset/account_asset.py @@ -254,9 +254,9 @@ class account_asset_asset(osv.osv): 'note': fields.text('Note'), 'category_id': fields.many2one('account.asset.category', 'Asset Category', required=True, change_default=True, readonly=True, states={'draft':[('readonly',False)]}), 'parent_id': fields.many2one('account.asset.asset', 'Parent Asset', readonly=True, states={'draft':[('readonly',False)]}), - 'child_ids': fields.one2many('account.asset.asset', 'parent_id', 'Children Assets'), + 'child_ids': fields.one2many('account.asset.asset', 'parent_id', 'Children Assets', copy=True), 'purchase_date': fields.date('Purchase Date', required=True, readonly=True, states={'draft':[('readonly',False)]}), - 'state': fields.selection([('draft','Draft'),('open','Running'),('close','Close')], 'Status', required=True, + 'state': fields.selection([('draft','Draft'),('open','Running'),('close','Close')], 'Status', required=True, copy=False, help="When an asset is created, the status is 'Draft'.\n" \ "If the asset is confirmed, the status goes in 'Running' and the depreciation lines can be posted in the accounting.\n" \ "You can manually close an asset when the depreciation is over. If the last line of depreciation is posted, the asset automatically goes in that status."), @@ -329,23 +329,13 @@ class account_asset_asset(osv.osv): res['value'] = {'prorata': False} return res - def copy(self, cr, uid, id, default=None, context=None): - if default is None: - default = {} - if context is None: - context = {} - default.update({'depreciation_line_ids': [], 'account_move_line_ids': [], 'history_ids': [], 'state': 'draft'}) - return super(account_asset_asset, self).copy(cr, uid, id, default, context=context) - def _compute_entries(self, cr, uid, ids, period_id, context=None): result = [] period_obj = self.pool.get('account.period') depreciation_obj = self.pool.get('account.asset.depreciation.line') period = period_obj.browse(cr, uid, period_id, context=context) depreciation_ids = depreciation_obj.search(cr, uid, [('asset_id', 'in', ids), ('depreciation_date', '<=', period.date_stop), ('depreciation_date', '>=', period.date_start), ('move_check', '=', False)], context=context) - if context is None: - context = {} - context.update({'depreciation_date':period.date_stop}) + context = dict(context or {}, depreciation_date=period.date_stop) return depreciation_obj.create_move(cr, uid, depreciation_ids, context=context) def create(self, cr, uid, vals, context=None): @@ -354,9 +344,7 @@ class account_asset_asset(osv.osv): return asset_id def open_entries(self, cr, uid, ids, context=None): - if context is None: - context = {} - context.update({'search_default_asset_id': ids, 'default_asset_id': ids}) + context = dict(context or {}, search_default_asset_id=ids, default_asset_id=ids) return { 'name': _('Journal Items'), 'view_type': 'form', @@ -392,9 +380,8 @@ class account_asset_depreciation_line(osv.osv): } def create_move(self, cr, uid, ids, context=None): + context = dict(context or {}) can_close = False - if context is None: - context = {} asset_obj = self.pool.get('account.asset.asset') period_obj = self.pool.get('account.period') move_obj = self.pool.get('account.move') @@ -466,8 +453,6 @@ class account_move_line(osv.osv): _inherit = 'account.move.line' _columns = { 'asset_id': fields.many2one('account.asset.asset', 'Asset', ondelete="restrict"), - 'entry_ids': fields.one2many('account.move.line', 'asset_id', 'Entries', readonly=True, states={'draft':[('readonly',False)]}), - } class account_asset_history(osv.osv): diff --git a/addons/account_bank_statement_extensions/account_bank_statement.py b/addons/account_bank_statement_extensions/account_bank_statement.py index 0b0386ecf8e..38cd68fbc2f 100644 --- a/addons/account_bank_statement_extensions/account_bank_statement.py +++ b/addons/account_bank_statement_extensions/account_bank_statement.py @@ -39,21 +39,27 @@ class account_bank_statement(osv.osv): return res def button_confirm_bank(self, cr, uid, ids, context=None): + bank_statement_line_obj = self.pool.get('account.bank.statement.line') super(account_bank_statement, self).button_confirm_bank(cr, uid, ids, context=context) for st in self.browse(cr, uid, ids, context=context): if st.line_ids: + line_ids = [l.id for l in st.line_ids] cr.execute("UPDATE account_bank_statement_line \ SET state='confirm' WHERE id in %s ", - (tuple([x.id for x in st.line_ids]),)) + (tuple(line_ids),)) + bank_statement_line_obj.invalidate_cache(cr, uid, ['state'], line_ids, context=context) return True def button_cancel(self, cr, uid, ids, context=None): + bank_statement_line_obj = self.pool.get('account.bank.statement.line') super(account_bank_statement, self).button_cancel(cr, uid, ids, context=context) for st in self.browse(cr, uid, ids, context=context): if st.line_ids: + line_ids = [l.id for l in st.line_ids] cr.execute("UPDATE account_bank_statement_line \ SET state='draft' WHERE id in %s ", - (tuple([x.id for x in st.line_ids]),)) + (tuple([line_ids]),)) + bank_statement_line_obj.invalidate_cache(cr, uid, ['state'], line_ids, context=context) return True @@ -65,7 +71,7 @@ class account_bank_statement_line_global(osv.osv): 'name': fields.char('OBI', required=True, help="Originator to Beneficiary Information"), 'code': fields.char('Code', size=64, required=True), 'parent_id': fields.many2one('account.bank.statement.line.global', 'Parent Code', ondelete='cascade'), - 'child_ids': fields.one2many('account.bank.statement.line.global', 'parent_id', 'Child Codes'), + 'child_ids': fields.one2many('account.bank.statement.line.global', 'parent_id', 'Child Codes', copy=True), 'type': fields.selection([ ('iso20022', 'ISO 20022'), ('coda', 'CODA'), @@ -110,7 +116,7 @@ class account_bank_statement_line(osv.osv): 'globalisation_amount': fields.related('globalisation_id', 'amount', type='float', relation='account.bank.statement.line.global', string='Glob. Amount', readonly=True), 'state': fields.selection([('draft', 'Draft'), ('confirm', 'Confirmed')], - 'Status', required=True, readonly=True), + 'Status', required=True, readonly=True, copy=False), 'counterparty_name': fields.char('Counterparty Name', size=35), 'counterparty_bic': fields.char('Counterparty BIC', size=11), 'counterparty_number': fields.char('Counterparty Number', size=34), diff --git a/addons/account_budget/account_budget.py b/addons/account_budget/account_budget.py index bed9085875e..d41e25e0f04 100644 --- a/addons/account_budget/account_budget.py +++ b/addons/account_budget/account_budget.py @@ -62,8 +62,8 @@ class crossovered_budget(osv.osv): 'validating_user_id': fields.many2one('res.users', 'Validate User', readonly=True), 'date_from': fields.date('Start Date', required=True, states={'done':[('readonly',True)]}), 'date_to': fields.date('End Date', required=True, states={'done':[('readonly',True)]}), - 'state' : fields.selection([('draft','Draft'),('cancel', 'Cancelled'),('confirm','Confirmed'),('validate','Validated'),('done','Done')], 'Status', select=True, required=True, readonly=True), - 'crossovered_budget_line': fields.one2many('crossovered.budget.lines', 'crossovered_budget_id', 'Budget Lines', states={'done':[('readonly',True)]}), + 'state' : fields.selection([('draft','Draft'),('cancel', 'Cancelled'),('confirm','Confirmed'),('validate','Validated'),('done','Done')], 'Status', select=True, required=True, readonly=True, copy=False), + 'crossovered_budget_line': fields.one2many('crossovered.budget.lines', 'crossovered_budget_id', 'Budget Lines', states={'done':[('readonly',True)]}, copy=True), 'company_id': fields.many2one('res.company', 'Company', required=True), } diff --git a/addons/account_check_writing/wizard/account_check_batch_printing.py b/addons/account_check_writing/wizard/account_check_batch_printing.py index bf59557d904..aab8ca277f2 100644 --- a/addons/account_check_writing/wizard/account_check_batch_printing.py +++ b/addons/account_check_writing/wizard/account_check_batch_printing.py @@ -33,7 +33,7 @@ class account_check_write(osv.osv_memory): def _get_next_number(self, cr, uid, context=None): dummy, sequence_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'account_check_writing', 'sequence_check_number') - return self.pool.get('ir.sequence').read(cr, uid, sequence_id, ['number_next'])['number_next'] + return self.pool.get('ir.sequence').read(cr, uid, [sequence_id], ['number_next'])[0]['number_next'] _defaults = { 'check_number': _get_next_number, @@ -47,7 +47,7 @@ class account_check_write(osv.osv_memory): #update the sequence to number the checks from the value encoded in the wizard dummy, sequence_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'account_check_writing', 'sequence_check_number') - increment = ir_sequence_obj.read(cr, uid, sequence_id, ['number_increment'])['number_increment'] + increment = ir_sequence_obj.read(cr, uid, [sequence_id], ['number_increment'])[0]['number_increment'] new_value = self.browse(cr, uid, ids[0], context=context).check_number ir_sequence_obj.write(cr, uid, sequence_id, {'number_next': new_value}) diff --git a/addons/account_followup/account_followup.py b/addons/account_followup/account_followup.py index dc00119c0bf..23fbeb598b8 100644 --- a/addons/account_followup/account_followup.py +++ b/addons/account_followup/account_followup.py @@ -19,6 +19,7 @@ # ############################################################################## +from openerp import api from openerp.osv import fields, osv from lxml import etree from openerp.tools.translate import _ @@ -28,7 +29,7 @@ class followup(osv.osv): _description = 'Account Follow-up' _rec_name = 'name' _columns = { - 'followup_line': fields.one2many('account_followup.followup.line', 'followup_id', 'Follow-up'), + 'followup_line': fields.one2many('account_followup.followup.line', 'followup_id', 'Follow-up', copy=True), 'company_id': fields.many2one('res.company', 'Company', required=True), 'name': fields.related('company_id', 'name', string = "Name", readonly=True, type="char"), } @@ -154,6 +155,7 @@ class res_partner(osv.osv): 'latest_followup_level_id_without_lit': latest_level_without_lit} return res + @api.cr_uid_ids_context def do_partner_manual_action(self, cr, uid, partner_ids, context=None): #partner_ids -> res.partner for partner in self.browse(cr, uid, partner_ids, context=context): @@ -190,6 +192,7 @@ class res_partner(osv.osv): } return self.pool['report'].get_action(cr, uid, [], 'account_followup.report_followup', data=datas, context=context) + @api.cr_uid_ids_context def do_partner_mail(self, cr, uid, partner_ids, context=None): if context is None: context = {} @@ -268,7 +271,7 @@ class res_partner(osv.osv): if date <= current_date and aml['balance'] > 0: strbegin = "" strend = "" - followup_table +="" + strbegin + str(aml['date']) + strend + strbegin + aml['name'] + strend + strbegin + aml['ref'] + strend + strbegin + str(date) + strend + strbegin + str(aml['balance']) + strend + strbegin + block + strend + "" + followup_table +="" + strbegin + str(aml['date']) + strend + strbegin + aml['name'] + strend + strbegin + (aml['ref'] or '') + strend + strbegin + str(date) + strend + strbegin + str(aml['balance']) + strend + strbegin + block + strend + "" total = reduce(lambda x, y: x+y['balance'], currency_dict['line'], 0.00) @@ -434,15 +437,17 @@ class res_partner(osv.osv): _columns = { 'payment_responsible_id':fields.many2one('res.users', ondelete='set null', string='Follow-up Responsible', help="Optionally you can assign a user to this field, which will make him responsible for the action.", - track_visibility="onchange"), - 'payment_note':fields.text('Customer Payment Promise', help="Payment Note", track_visibility="onchange"), - 'payment_next_action':fields.text('Next Action', + track_visibility="onchange", copy=False), + 'payment_note':fields.text('Customer Payment Promise', help="Payment Note", track_visibility="onchange", copy=False), + 'payment_next_action':fields.text('Next Action', copy=False, help="This is the next action to be taken. It will automatically be set when the partner gets a follow-up level that requires a manual action. ", track_visibility="onchange"), - 'payment_next_action_date':fields.date('Next Action Date', - help="This is when the manual follow-up is needed. " \ - "The date will be set to the current date when the partner gets a follow-up level that requires a manual action. "\ - "Can be practical to set manually e.g. to see if he keeps his promises."), + 'payment_next_action_date': fields.date('Next Action Date', copy=False, + help="This is when the manual follow-up is needed. " + "The date will be set to the current date when the partner " + "gets a follow-up level that requires a manual action. " + "Can be practical to set manually e.g. to see if he keeps " + "his promises."), 'unreconciled_aml_ids':fields.one2many('account.move.line', 'partner_id', domain=['&', ('reconcile_id', '=', False), '&', ('account_id.active','=', True), '&', ('account_id.type', '=', 'receivable'), ('state', '!=', 'draft')]), 'latest_followup_date':fields.function(_get_latest, method=True, type='date', string="Latest Follow-up Date", diff --git a/addons/account_followup/report/account_followup_print.py b/addons/account_followup/report/account_followup_print.py index 4e856853915..bf20527d3fa 100644 --- a/addons/account_followup/report/account_followup_print.py +++ b/addons/account_followup/report/account_followup_print.py @@ -75,9 +75,7 @@ class report_rappel(report_sxw.rml_parse): return [{'line': lines} for lines in lines_per_currency.values()] def _get_text(self, stat_line, followup_id, context=None): - if context is None: - context = {} - context.update({'lang': stat_line.partner_id.lang}) + context = dict(context or {}, lang=stat_line.partner_id.lang) fp_obj = self.pool['account_followup.followup'] fp_line = fp_obj.browse(self.cr, self.uid, followup_id, context=context).followup_line if not fp_line: diff --git a/addons/account_followup/tests/test_account_followup.py b/addons/account_followup/tests/test_account_followup.py index b3025021278..697fb433362 100644 --- a/addons/account_followup/tests/test_account_followup.py +++ b/addons/account_followup/tests/test_account_followup.py @@ -42,7 +42,7 @@ class TestAccountFollowup(TransactionCase): 'quantity': 5, 'price_unit':200 })]}) - self.registry('account.invoice').signal_invoice_open(cr, uid, [self.invoice_id]) + self.registry('account.invoice').signal_workflow(cr, uid, [self.invoice_id], 'invoice_open') self.voucher = self.registry("account.voucher") @@ -112,8 +112,7 @@ class TestAccountFollowup(TransactionCase): partner_rec = self.partner.browse(cr, uid, self.partner_id) self.run_wizard_three_times() self.partner.action_done(cr, uid, self.partner_id) - self.assertEqual(partner_rec.payment_next_action, - "", "Manual action not emptied") + self.assertFalse(partner_rec.payment_next_action, "Manual action not emptied") self.assertFalse(partner_rec.payment_responsible_id) self.assertFalse(partner_rec.payment_next_action_date) diff --git a/addons/account_followup/wizard/account_followup_print.py b/addons/account_followup/wizard/account_followup_print.py index 7b7cb8fc77e..83100723edb 100644 --- a/addons/account_followup/wizard/account_followup_print.py +++ b/addons/account_followup/wizard/account_followup_print.py @@ -204,15 +204,14 @@ class account_followup_print(osv.osv_memory): return len(partners_to_clear) def do_process(self, cr, uid, ids, context=None): - if context is None: - context = {} + context = dict(context or {}) #Get partners tmp = self._get_partners_followp(cr, uid, ids, context=context) partner_list = tmp['partner_ids'] to_update = tmp['to_update'] date = self.browse(cr, uid, ids, context=context)[0].date - data = self.read(cr, uid, ids, [], context=context)[0] + data = self.read(cr, uid, ids, context=context)[0] data['followup_id'] = data['followup_id'][0] #Update partners diff --git a/addons/account_payment/account_payment.py b/addons/account_payment/account_payment.py index d2b2786e995..de72960a9c6 100644 --- a/addons/account_payment/account_payment.py +++ b/addons/account_payment/account_payment.py @@ -87,13 +87,13 @@ class payment_order(osv.osv): _columns = { 'date_scheduled': fields.date('Scheduled Date', states={'done':[('readonly', True)]}, help='Select a date if you have chosen Preferred Date to be fixed.'), - 'reference': fields.char('Reference', required=1, states={'done': [('readonly', True)]}), + 'reference': fields.char('Reference', required=1, states={'done': [('readonly', True)]}, copy=False), 'mode': fields.many2one('payment.mode', 'Payment Mode', select=True, required=1, states={'done': [('readonly', True)]}, help='Select the Payment Mode to be applied.'), 'state': fields.selection([ ('draft', 'Draft'), ('cancel', 'Cancelled'), ('open', 'Confirmed'), - ('done', 'Done')], 'Status', select=True, + ('done', 'Done')], 'Status', select=True, copy=False, help='When an order is placed the status is \'Draft\'.\n Once the bank is confirmed the status is set to \'Confirmed\'.\n Then the order is paid the status is \'Done\'.'), 'line_ids': fields.one2many('payment.line', 'order_id', 'Payment lines', states={'done': [('readonly', True)]}), 'total': fields.function(_total, string="Total", type='float'), @@ -132,19 +132,9 @@ class payment_order(osv.osv): def set_done(self, cr, uid, ids, *args): self.write(cr, uid, ids, {'date_done': time.strftime('%Y-%m-%d')}) - self.signal_done(cr, uid, [ids[0]]) + self.signal_workflow(cr, uid, ids, 'done') return True - def copy(self, cr, uid, id, default=None, context=None): - if default is None: - default = {} - default.update({ - 'state': 'draft', - 'line_ids': [], - 'reference': self.pool.get('ir.sequence').get(cr, uid, 'payment.order') - }) - return super(payment_order, self).copy(cr, uid, id, default, context=context) - def write(self, cr, uid, ids, vals, context=None): if context is None: context = {} @@ -179,7 +169,7 @@ class payment_line(osv.osv): "due_date": "date_maturity", "reference": "ref"}.get(orig, orig) - def info_owner(self, cr, uid, ids, name=None, args=None, context=None): + def _info_owner(self, cr, uid, ids, name=None, args=None, context=None): result = {} for line in self.browse(cr, uid, ids, context=context): owner = line.order_id.mode.bank_id.partner_id @@ -197,7 +187,7 @@ class payment_line(osv.osv): cntry = partner_record.country_id and partner_record.country_id.name or '' return partner_record.name + "\n" + st + " " + st1 + "\n" + zip_city + "\n" +cntry - def info_partner(self, cr, uid, ids, name=None, args=None, context=None): + def _info_partner(self, cr, uid, ids, name=None, args=None, context=None): result = {} for line in self.browse(cr, uid, ids, context=context): result[line.id] = False @@ -322,8 +312,8 @@ class payment_line(osv.osv): type='date', help="Invoice Effective Date"), 'ml_maturity_date': fields.function(_get_ml_maturity_date, type='date', string='Due Date'), 'ml_inv_ref': fields.function(_get_ml_inv_ref, type='many2one', relation='account.invoice', string='Invoice Ref.'), - 'info_owner': fields.function(info_owner, string="Owner Account", type="text", help='Address of the Main Partner'), - 'info_partner': fields.function(info_partner, string="Destination Account", type="text", help='Address of the Ordering Customer.'), + 'info_owner': fields.function(_info_owner, string="Owner Account", type="text", help='Address of the Main Partner'), + 'info_partner': fields.function(_info_partner, string="Destination Account", type="text", help='Address of the Ordering Customer.'), 'date': fields.date('Payment Date', help="If no payment date is specified, the bank will treat this payment line directly"), 'create_date': fields.datetime('Created', readonly=True), 'state': fields.selection([('normal','Free'), ('structured','Structured')], 'Communication Type', required=True), diff --git a/addons/account_payment/test/payment_order_process.yml b/addons/account_payment/test/payment_order_process.yml index 2bb04807a64..5aaf97b9e71 100644 --- a/addons/account_payment/test/payment_order_process.yml +++ b/addons/account_payment/test/payment_order_process.yml @@ -79,7 +79,7 @@ - I create a bank statement. - - !record {model: account.bank.statement, id: account_bank_statement_1}: + !record {model: account.bank.statement, id: account_bank_statement_1, view: False}: balance_end_real: 0.0 balance_start: 0.0 date: !eval time.strftime('%Y-%m-%d') diff --git a/addons/account_payment/wizard/account_payment_order.py b/addons/account_payment/wizard/account_payment_order.py index 8d5df048fe0..78b634d44fc 100644 --- a/addons/account_payment/wizard/account_payment_order.py +++ b/addons/account_payment/wizard/account_payment_order.py @@ -107,7 +107,7 @@ class payment_order_create(osv.osv_memory): domain = [('reconcile_id', '=', False), ('account_id.type', '=', 'payable'), ('credit', '>', 0), ('account_id.reconcile', '=', True)] domain = domain + ['|', ('date_maturity', '<=', search_due_date), ('date_maturity', '=', False)] line_ids = line_obj.search(cr, uid, domain, context=context) - context.update({'line_ids': line_ids}) + context = dict(context, line_ids=line_ids) model_data_ids = mod_obj.search(cr, uid,[('model', '=', 'ir.ui.view'), ('name', '=', 'view_create_payment_order_lines')], context=context) resource_id = mod_obj.read(cr, uid, model_data_ids, fields=['res_id'], context=context)[0]['res_id'] return {'name': _('Entry Lines'), diff --git a/addons/account_payment/wizard/account_payment_populate_statement.py b/addons/account_payment/wizard/account_payment_populate_statement.py index ef068a55b2c..6a049ff78a9 100644 --- a/addons/account_payment/wizard/account_payment_populate_statement.py +++ b/addons/account_payment/wizard/account_payment_populate_statement.py @@ -62,7 +62,7 @@ class account_payment_populate_statement(osv.osv_memory): if context is None: context = {} - data = self.read(cr, uid, ids, [], context=context)[0] + data = self.read(cr, uid, ids, context=context)[0] line_ids = data['lines'] if not line_ids: return {'type': 'ir.actions.act_window_close'} @@ -77,7 +77,7 @@ class account_payment_populate_statement(osv.osv_memory): if not line.move_line_id.id: continue - context.update({'move_line_ids': [line.move_line_id.id]}) + context = dict(context, move_line_ids=[line.move_line_id.id]) result = voucher_obj.onchange_partner_id(cr, uid, [], partner_id=line.partner_id.id, journal_id=statement.journal_id.id, amount=abs(amount), currency_id= statement.currency.id, ttype='payment', date=line.ml_maturity_date, context=context) if line.move_line_id: diff --git a/addons/account_sequence/account_sequence.py b/addons/account_sequence/account_sequence.py index 1b60e51e124..c85320d92c3 100644 --- a/addons/account_sequence/account_sequence.py +++ b/addons/account_sequence/account_sequence.py @@ -26,7 +26,9 @@ class account_move(osv.osv): _inherit = 'account.move' _columns = { - 'internal_sequence_number': fields.char('Internal Number', readonly=True, help='Internal Sequence Number'), + 'internal_sequence_number': fields.char('Internal Number', + readonly=True, copy=False, + help='Internal Sequence Number'), } def post(self, cr, uid, ids, context=None): diff --git a/addons/account_voucher/account_voucher.py b/addons/account_voucher/account_voucher.py index 9fde90231b2..275e002695e 100644 --- a/addons/account_voucher/account_voucher.py +++ b/addons/account_voucher/account_voucher.py @@ -335,10 +335,13 @@ class account_voucher(osv.osv): ('receipt','Receipt'), ],'Default Type', readonly=True, states={'draft':[('readonly',False)]}), 'name':fields.char('Memo', readonly=True, states={'draft':[('readonly',False)]}), - 'date':fields.date('Date', readonly=True, select=True, states={'draft':[('readonly',False)]}, help="Effective date for accounting entries"), + 'date':fields.date('Date', readonly=True, select=True, states={'draft':[('readonly',False)]}, + help="Effective date for accounting entries", copy=False), 'journal_id':fields.many2one('account.journal', 'Journal', required=True, readonly=True, states={'draft':[('readonly',False)]}), 'account_id':fields.many2one('account.account', 'Account', required=True, readonly=True, states={'draft':[('readonly',False)]}), - 'line_ids':fields.one2many('account.voucher.line','voucher_id','Voucher Lines', readonly=True, states={'draft':[('readonly',False)]}), + 'line_ids':fields.one2many('account.voucher.line', 'voucher_id', 'Voucher Lines', + readonly=True, copy=True, + states={'draft':[('readonly',False)]}), 'line_cr_ids':fields.one2many('account.voucher.line','voucher_id','Credits', domain=[('type','=','cr')], context={'default_type':'cr'}, readonly=True, states={'draft':[('readonly',False)]}), 'line_dr_ids':fields.one2many('account.voucher.line','voucher_id','Debits', @@ -352,16 +355,17 @@ class account_voucher(osv.osv): ('cancel','Cancelled'), ('proforma','Pro-forma'), ('posted','Posted') - ], 'Status', readonly=True, track_visibility='onchange', + ], 'Status', readonly=True, track_visibility='onchange', copy=False, help=' * The \'Draft\' status is used when a user is encoding a new and unconfirmed Voucher. \ \n* The \'Pro-forma\' when voucher is in Pro-forma status,voucher does not have an voucher number. \ \n* The \'Posted\' status is used when user create voucher,a voucher number is generated and voucher entries are created in account \ \n* The \'Cancelled\' status is used when user cancel voucher.'), 'amount': fields.float('Total', digits_compute=dp.get_precision('Account'), required=True, readonly=True, states={'draft':[('readonly',False)]}), 'tax_amount':fields.float('Tax Amount', digits_compute=dp.get_precision('Account'), readonly=True, states={'draft':[('readonly',False)]}), - 'reference': fields.char('Ref #', readonly=True, states={'draft':[('readonly',False)]}, help="Transaction reference number."), - 'number': fields.char('Number', readonly=True,), - 'move_id':fields.many2one('account.move', 'Account Entry'), + 'reference': fields.char('Ref #', readonly=True, states={'draft':[('readonly',False)]}, + help="Transaction reference number.", copy=False), + 'number': fields.char('Number', readonly=True, copy=False), + 'move_id':fields.many2one('account.move', 'Account Entry', copy=False), 'move_ids': fields.related('move_id','line_id', type='one2many', relation='account.move.line', string='Journal Items', readonly=True), 'partner_id':fields.many2one('res.partner', 'Partner', change_default=1, readonly=True, states={'draft':[('readonly',False)]}), 'audit': fields.related('move_id','to_check', type='boolean', help='Check this box if you are unsure of that journal entry and if you want to note it as \'to be reviewed\' by an accounting expert.', relation='account.move', string='To Review'), @@ -573,7 +577,7 @@ class account_voucher(osv.osv): ctx.update({'date': date}) #read the voucher rate with the right date in the context currency_id = currency_id or self.pool.get('res.company').browse(cr, uid, company_id, context=ctx).currency_id.id - voucher_rate = self.pool.get('res.currency').read(cr, uid, currency_id, ['rate'], context=ctx)['rate'] + voucher_rate = self.pool.get('res.currency').read(cr, uid, [currency_id], ['rate'], context=ctx)[0]['rate'] ctx.update({ 'voucher_special_currency': payment_rate_currency_id, 'voucher_special_currency_rate': rate * voucher_rate}) @@ -615,7 +619,7 @@ class account_voucher(osv.osv): 'payment_rate_currency_id': payment_rate_currency_id }) #read the voucher rate with the right date in the context - voucher_rate = self.pool.get('res.currency').read(cr, uid, currency_id, ['rate'], context=ctx)['rate'] + voucher_rate = self.pool.get('res.currency').read(cr, uid, [currency_id], ['rate'], context=ctx)[0]['rate'] ctx.update({ 'voucher_special_currency_rate': payment_rate * voucher_rate, 'voucher_special_currency': payment_rate_currency_id}) @@ -847,7 +851,7 @@ class account_voucher(osv.osv): ctx = context.copy() ctx.update({'date': date}) #read the voucher rate with the right date in the context - voucher_rate = self.pool.get('res.currency').read(cr, uid, currency_id, ['rate'], context=ctx)['rate'] + voucher_rate = self.pool.get('res.currency').read(cr, uid, [currency_id], ['rate'], context=ctx)[0]['rate'] ctx.update({ 'voucher_special_currency_rate': payment_rate * voucher_rate, 'voucher_special_currency': payment_rate_currency_id}) @@ -922,7 +926,7 @@ class account_voucher(osv.osv): return vals def button_proforma_voucher(self, cr, uid, ids, context=None): - self.signal_proforma_voucher(cr, uid, ids) + self.signal_workflow(cr, uid, ids, 'proforma_voucher') return {'type': 'ir.actions.act_window_close'} def proforma_voucher(self, cr, uid, ids, context=None): @@ -1182,7 +1186,7 @@ class account_voucher(osv.osv): tot_line = line_total rec_lst_ids = [] - date = self.read(cr, uid, voucher_id, ['date'], context=context)['date'] + date = self.read(cr, uid, [voucher_id], ['date'], context=context)[0]['date'] ctx = context.copy() ctx.update({'date': date}) voucher = self.pool.get('account.voucher').browse(cr, uid, voucher_id, context=ctx) @@ -1419,22 +1423,6 @@ class account_voucher(osv.osv): reconcile = move_line_pool.reconcile_partial(cr, uid, rec_ids, writeoff_acc_id=voucher.writeoff_acc_id.id, writeoff_period_id=voucher.period_id.id, writeoff_journal_id=voucher.journal_id.id) return True - def copy(self, cr, uid, id, default=None, context=None): - if default is None: - default = {} - default.update({ - 'state': 'draft', - 'number': False, - 'move_id': False, - 'line_cr_ids': False, - 'line_dr_ids': False, - 'reference': False - }) - if 'date' not in default: - default['date'] = time.strftime('%Y-%m-%d') - return super(account_voucher, self).copy(cr, uid, id, default, context) - - class account_voucher_line(osv.osv): _name = 'account.voucher.line' _description = 'Voucher Lines' @@ -1495,7 +1483,7 @@ class account_voucher_line(osv.osv): 'reconcile': fields.boolean('Full Reconcile'), 'type':fields.selection([('dr','Debit'),('cr','Credit')], 'Dr/Cr'), 'account_analytic_id': fields.many2one('account.analytic.account', 'Analytic Account'), - 'move_line_id': fields.many2one('account.move.line', 'Journal Item'), + 'move_line_id': fields.many2one('account.move.line', 'Journal Item', copy=False), 'date_original': fields.related('move_line_id','date', type='date', relation='account.move.line', string='Date', readonly=1), 'date_due': fields.related('move_line_id','date_maturity', type='date', relation='account.move.line', string='Due Date', readonly=1), 'amount_original': fields.function(_compute_balance, multi='dc', type='float', string='Original Amount', store=True, digits_compute=dp.get_precision('Account')), diff --git a/addons/account_voucher/test/case1_usd_usd.yml b/addons/account_voucher/test/case1_usd_usd.yml index 44269104370..84ae118ab16 100644 --- a/addons/account_voucher/test/case1_usd_usd.yml +++ b/addons/account_voucher/test/case1_usd_usd.yml @@ -182,7 +182,7 @@ - !python {model: account.voucher}: | voucher = self.search(cr, uid, [('name', '=', 'First payment: Case 1 USD/USD'), ('partner_id', '=', ref('base.res_partner_19'))]) - self.signal_proforma_voucher(cr, uid, voucher) + self.signal_workflow(cr, uid, voucher, 'proforma_voucher') - I check that the move of my first voucher is valid - @@ -276,7 +276,7 @@ - !python {model: account.voucher}: | voucher = self.search(cr, uid, [('name', '=', 'Second payment: Case 1'), ('partner_id', '=', ref('base.res_partner_19'))]) - self.signal_proforma_voucher(cr, uid, voucher) + self.signal_workflow(cr, uid, voucher, 'proforma_voucher') - I check that the move of my second voucher is valid - diff --git a/addons/account_voucher/test/case1_usd_usd_payment_rate.yml b/addons/account_voucher/test/case1_usd_usd_payment_rate.yml index 037fc1497ec..c0aa617783c 100644 --- a/addons/account_voucher/test/case1_usd_usd_payment_rate.yml +++ b/addons/account_voucher/test/case1_usd_usd_payment_rate.yml @@ -191,7 +191,7 @@ - !python {model: account.voucher}: | voucher = ref('account_voucher_1_case1_payment_rate') - self.signal_proforma_voucher(cr, uid, [voucher]) + self.signal_workflow(cr, uid, [voucher], 'proforma_voucher') - I check that the move of my first voucher is valid - diff --git a/addons/account_voucher/test/case2_suppl_usd_eur.yml b/addons/account_voucher/test/case2_suppl_usd_eur.yml index c13bfcbd5d4..836bee35d30 100644 --- a/addons/account_voucher/test/case2_suppl_usd_eur.yml +++ b/addons/account_voucher/test/case2_suppl_usd_eur.yml @@ -161,7 +161,7 @@ - !python {model: account.voucher}: | voucher = self.search(cr, uid, [('name', '=', 'First payment: Case 2 SUPPL USD/EUR'), ('partner_id', '=', ref('base.res_partner_19'))]) - self.signal_proforma_voucher(cr, uid, voucher) + self.signal_workflow(cr, uid, voucher, 'proforma_voucher') - I check that the move of my voucher is valid - @@ -263,7 +263,7 @@ - !python {model: account.voucher}: | voucher = self.search(cr, uid, [('name', '=', 'Second payment: Case 2 SUPPL USD/EUR'), ('partner_id', '=', ref('base.res_partner_19'))]) - self.signal_proforma_voucher(cr, uid, voucher) + self.signal_workflow(cr, uid, voucher, 'proforma_voucher') - I check that my voucher state is posted - diff --git a/addons/account_voucher/test/case2_usd_eur_debtor_in_eur.yml b/addons/account_voucher/test/case2_usd_eur_debtor_in_eur.yml index f523e312933..45c86eb7ad0 100644 --- a/addons/account_voucher/test/case2_usd_eur_debtor_in_eur.yml +++ b/addons/account_voucher/test/case2_usd_eur_debtor_in_eur.yml @@ -182,7 +182,7 @@ !python {model: account.voucher}: | from openerp import netsvc voucher = self.search(cr, uid, [('name', '=', 'First payment: Case 2 USD/EUR DR EUR'), ('partner_id', '=', ref('base.res_partner_19'))]) - self.signal_proforma_voucher(cr, uid, voucher) + self.signal_workflow(cr, uid, voucher, 'proforma_voucher') - I check that the move of my voucher is valid - @@ -257,7 +257,7 @@ !python {model: account.voucher}: | from openerp import netsvc voucher = self.search(cr, uid, [('name', '=', 'Second payment: Case 2 SUPPL USD/EUR DR EUR'), ('partner_id', '=', ref('base.res_partner_19'))]) - self.signal_proforma_voucher(cr, uid, voucher) + self.signal_workflow(cr, uid, voucher, 'proforma_voucher') - I check that my voucher state is posted - diff --git a/addons/account_voucher/test/case2_usd_eur_debtor_in_usd.yml b/addons/account_voucher/test/case2_usd_eur_debtor_in_usd.yml index 52c416e06cc..f353cd8c6fc 100644 --- a/addons/account_voucher/test/case2_usd_eur_debtor_in_usd.yml +++ b/addons/account_voucher/test/case2_usd_eur_debtor_in_usd.yml @@ -179,7 +179,7 @@ - !python {model: account.voucher}: | voucher = self.search(cr, uid, [('name', '=', 'First payment: Case 2 USD/EUR DR USD'), ('partner_id', '=', ref('base.res_partner_19'))]) - self.signal_proforma_voucher(cr, uid, voucher) + self.signal_workflow(cr, uid, voucher, 'proforma_voucher') - I check that the move of my voucher is valid - @@ -266,7 +266,7 @@ - !python {model: account.voucher}: | voucher = self.search(cr, uid, [('name', '=', 'Second payment: Case 2 SUPPL USD/EUR DR USD'), ('partner_id', '=', ref('base.res_partner_19'))]) - self.signal_proforma_voucher(cr, uid, voucher) + self.signal_workflow(cr, uid, voucher, 'proforma_voucher') - I check that my voucher state is posted - diff --git a/addons/account_voucher/test/case3_eur_eur.yml b/addons/account_voucher/test/case3_eur_eur.yml index 99153ceeca4..064b79ed305 100644 --- a/addons/account_voucher/test/case3_eur_eur.yml +++ b/addons/account_voucher/test/case3_eur_eur.yml @@ -141,7 +141,7 @@ - !python {model: account.voucher}: | voucher = self.search(cr, uid, [('name', '=', 'First payment: Case 3'),('partner_id', '=', ref('base.res_partner_19'))]) - self.signal_proforma_voucher(cr, uid, voucher) + self.signal_workflow(cr, uid, voucher, 'proforma_voucher') - I check that the move of my first voucher is valid - @@ -228,7 +228,8 @@ - !python {model: account.voucher}: | voucher = self.search(cr, uid, [('name', '=', 'Second payment: Case 3'), ('partner_id', '=', ref('base.res_partner_19'))]) - self.signal_proforma_voucher(cr, uid, voucher) + self.signal_workflow(cr, uid, voucher, 'proforma_voucher') + - I check that the move of my second voucher is valid - diff --git a/addons/account_voucher/test/case4_cad_chf.yml b/addons/account_voucher/test/case4_cad_chf.yml index 90c36e371bf..476a22c3f15 100644 --- a/addons/account_voucher/test/case4_cad_chf.yml +++ b/addons/account_voucher/test/case4_cad_chf.yml @@ -88,12 +88,13 @@ I check that first invoice move is correct for debtor account (debit - credit == 149.39) - !python {model: account.invoice}: | + from openerp.tools import float_compare invoice_id = self.browse(cr, uid, ref("account_first_invoice_jan_cad")) assert invoice_id.move_id, "Move not created for open invoice" move_line_obj = self.pool.get('account.move.line') move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('account_id', '=', invoice_id.account_id.id)]) move_line = move_line_obj.browse(cr, uid, move_lines[0]) - assert (move_line.debit - move_line.credit == 149.39), "Invoice move is incorrect for debtors account" + assert float_compare(move_line.debit - move_line.credit, 149.39, 2) == 0, "Invoice move is incorrect for debtors account" - I set the context that will be used for the encoding of all the vouchers of this file - @@ -142,9 +143,9 @@ I confirm the voucher - !python {model: account.voucher}: | - from openerp import netsvc voucher = self.search(cr, uid, [('name', '=', 'First payment: Case 4'), ('partner_id', '=', ref('base.res_partner_19'))]) - self.signal_proforma_voucher(cr, uid, voucher) + self.signal_workflow(cr, uid, voucher, 'proforma_voucher') + - I check that the move of my voucher is valid - @@ -166,15 +167,17 @@ I check that my writeoff is correct. 11.05 credit and -13.26 amount_currency - !python {model: account.voucher}: | + from openerp.tools import float_compare voucher = self.search(cr, uid, [('name', '=', 'First payment: Case 4'), ('partner_id', '=', ref('base.res_partner_19'))]) voucher_id = self.browse(cr, uid, voucher[0]) move_line_obj = self.pool.get('account.move.line') move_lines = move_line_obj.search(cr, uid, [('move_id', '=', voucher_id.move_id.id)]) + assert move_lines, "Voucher move has no lines" for move_line in move_line_obj.browse(cr, uid, move_lines): - if move_line.amount_currency == 200: - assert move_line.debit == 160.00, "Bank account has wrong entry." - elif move_line.amount_currency == -298.78: - assert move_line.credit == 149.39, "Debtor account has wrong entry." + if float_compare(move_line.amount_currency, 200, 2) == 0: + assert float_compare(move_line.debit, 160.00, 2) == 0, "Bank account has wrong entry." + elif float_compare(move_line.amount_currency, -298.78, 2) == 0: + assert float_compare(move_line.credit, 149.39, 2) == 0, "Debtor account has wrong entry." elif move_line.debit == 0.00 and move_line.credit == 0.00: assert move_line.amount_currency == 98.78, "Incorrect Currency Difference, got %s as amount_currency (expected 98.78)." % (move_line.amount_currency) assert move_line.currency_id.id == ref('base.CAD'), "Incorrect Currency Difference, got %s (expected 'CAD')" % (move_line.currency_id.name) diff --git a/addons/account_voucher/test/case5_suppl_usd_usd.yml b/addons/account_voucher/test/case5_suppl_usd_usd.yml index 917e2366f56..c20d07a8192 100644 --- a/addons/account_voucher/test/case5_suppl_usd_usd.yml +++ b/addons/account_voucher/test/case5_suppl_usd_usd.yml @@ -16,14 +16,14 @@ - !record {model: res.currency.rate, id: nov_usd}: currency_id: base.USD - name: !eval "'%s-11-01' %(datetime.now().year)" + name: !eval "'%s-11-01 00:00:00' %(datetime.now().year)" rate: 1.8 - I create currency USD in OpenERP for December of 1.5 Rate - !record {model: res.currency.rate, id: dec_usd}: currency_id: base.USD - name: !eval "'%s-12-01' %(datetime.now().year)" + name: !eval "'%s-12-01 00:00:00' %(datetime.now().year)" rate: 1.5 - I set the income and expense currency accounts on the main company @@ -92,13 +92,16 @@ I check that first invoice move is correct for creditor account(debit - credit == -555.56) - !python {model: account.invoice}: | + from openerp.tools import float_compare invoice_id = self.browse(cr, uid, ref("account_supplier_invoice_november")) assert invoice_id.move_id, "Move not created for open invoice" move_line_obj = self.pool.get('account.move.line') move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('account_id', '=', invoice_id.account_id.id)]) move_line = move_line_obj.browse(cr, uid, move_lines[0]) - assert (move_line.debit - move_line.credit == -555.56), "Invoice move is incorrect for creditor account" - assert (move_line.amount_currency == -1000), "Amount currency is incorrect for creditor account" + assert float_compare(move_line.debit - move_line.credit, -555.56, 2) == 0, \ + "Invoice move is incorrect for creditor account" + assert float_compare(move_line.amount_currency, -1000, 2) == 0, \ + "Amount currency is incorrect for creditor account" - I set the context that will be used for the encoding of all the vouchers of this file - diff --git a/addons/account_voucher/test/case_eur_usd.yml b/addons/account_voucher/test/case_eur_usd.yml index 825fae1d1af..69304c342c9 100644 --- a/addons/account_voucher/test/case_eur_usd.yml +++ b/addons/account_voucher/test/case_eur_usd.yml @@ -107,6 +107,7 @@ for item in voucher.line_cr_ids: if item.amount_unreconciled == 1400: data += [(item.id, 1400)] + assert data, "Credit line not found" for line_id, amount in data: self.pool.get('account.voucher.line').write(cr, uid, [line_id], {'amount': amount}) assert (voucher.state=='draft'), "Voucher is not in draft state" @@ -170,5 +171,6 @@ move_line_obj = self.pool.get('account.move.line') move_lines = move_line_obj.search(cr, uid, [('move_id', '=', invoice_id.move_id.id), ('invoice', '=', invoice_id.id), ('account_id', '=', invoice_id.account_id.id)]) move_line = move_line_obj.browse(cr, uid, move_lines[0]) - assert (move_line.amount_residual_currency == 0.0 and move_line.amount_residual == 0.0 and invoice_id.state == 'paid') , "Residual amount is not correct for Invoice" - + assert move_line.amount_residual_currency == 0.0, "Residual amount is not correct for Invoice" + assert move_line.amount_residual == 0.0, "Residual amount is not correct for Invoice" + assert invoice_id.state == 'paid', "Invoice is not paid" diff --git a/addons/account_voucher/test/sales_payment.yml b/addons/account_voucher/test/sales_payment.yml index ed9305a320c..7bd0e18a5c4 100644 --- a/addons/account_voucher/test/sales_payment.yml +++ b/addons/account_voucher/test/sales_payment.yml @@ -68,7 +68,7 @@ voucher_id = self.browse(cr, uid, id) assert (voucher_id.writeoff_amount == 0.0), "Writeoff amount is not 0.0" assert (voucher_id.state=='draft'), "Voucher is not in draft state" - self.signal_proforma_voucher(cr, uid, [voucher_id.id]) + voucher_id.signal_workflow('proforma_voucher') - Finally i will Confirm the state of the invoice is paid diff --git a/addons/account_voucher/test/sales_receipt.yml b/addons/account_voucher/test/sales_receipt.yml index e75eda2aac0..9bae2c3549b 100644 --- a/addons/account_voucher/test/sales_receipt.yml +++ b/addons/account_voucher/test/sales_receipt.yml @@ -69,7 +69,7 @@ id = self.create(cr, uid, vals) voucher_id = self.browse(cr, uid, id) assert (voucher_id.state=='draft'), "Voucher is not in draft state" - self.signal_proforma_voucher(cr, uid, [voucher_id.id]) + voucher_id.signal_workflow('proforma_voucher') - I check that move lines are reconciled meaning voucher is paid - diff --git a/addons/analytic/analytic.py b/addons/analytic/analytic.py index 1d728ba1e7c..e31efa0eb2a 100644 --- a/addons/analytic/analytic.py +++ b/addons/analytic/analytic.py @@ -159,7 +159,8 @@ class account_analytic_account(osv.osv): if account.company_id.currency_id.id != value: raise osv.except_osv(_('Error!'), _("If you set a company, the currency selected has to be the same as it's currency. \nYou can remove the company belonging, and thus change the currency, only on analytic account of type 'view'. This can be really useful for consolidation purposes of several companies charts with different currencies, for example.")) if value: - return cr.execute("""update account_analytic_account set currency_id=%s where id=%s""", (value, account.id, )) + cr.execute("""update account_analytic_account set currency_id=%s where id=%s""", (value, account.id)) + self.invalidate_cache(cr, uid, ['currency_id'], [account.id], context=context) def _currency(self, cr, uid, ids, field_name, arg, context=None): result = {} @@ -173,7 +174,7 @@ class account_analytic_account(osv.osv): _columns = { 'name': fields.char('Account/Contract Name', required=True, track_visibility='onchange'), 'complete_name': fields.function(_get_full_name, type='char', string='Full Name'), - 'code': fields.char('Reference', select=True, track_visibility='onchange'), + 'code': fields.char('Reference', select=True, track_visibility='onchange', copy=False), 'type': fields.selection([('view','Analytic View'), ('normal','Analytic Account'),('contract','Contract or Project'),('template','Template of Contract')], 'Type of Account', required=True, help="If you select the View Type, it means you won\'t allow to create journal entries using that account.\n"\ "The type 'Analytic account' stands for usual accounts that you only want to use in accounting.\n"\ @@ -196,7 +197,14 @@ class account_analytic_account(osv.osv): 'date_start': fields.date('Start Date'), 'date': fields.date('Expiration Date', select=True, track_visibility='onchange'), 'company_id': fields.many2one('res.company', 'Company', required=False), #not required because we want to allow different companies to use the same chart of account, except for leaf accounts. - 'state': fields.selection([('template', 'Template'),('draft','New'),('open','In Progress'),('pending','To Renew'),('close','Closed'),('cancelled', 'Cancelled')], 'Status', required=True, track_visibility='onchange'), + 'state': fields.selection([('template', 'Template'), + ('draft','New'), + ('open','In Progress'), + ('pending','To Renew'), + ('close','Closed'), + ('cancelled', 'Cancelled')], + 'Status', required=True, + track_visibility='onchange', copy=False), 'currency_id': fields.function(_currency, fnct_inv=_set_company_currency, #the currency_id field is readonly except if it's a view account and if there is no company store = { 'res.company': (_get_analytic_account, ['currency_id'], 10), @@ -266,10 +274,7 @@ class account_analytic_account(osv.osv): if not default: default = {} analytic = self.browse(cr, uid, id, context=context) - default.update( - code=False, - line_ids=[], - name=_("%s (copy)") % (analytic['name'])) + default['name'] = _("%s (copy)") % analytic['name'] return super(account_analytic_account, self).copy(cr, uid, id, default, context=context) def on_change_company(self, cr, uid, id, company_id): diff --git a/addons/analytic_contract_hr_expense/analytic_contract_hr_expense.py b/addons/analytic_contract_hr_expense/analytic_contract_hr_expense.py index 3682755b9c4..5c251708d91 100644 --- a/addons/analytic_contract_hr_expense/analytic_contract_hr_expense.py +++ b/addons/analytic_contract_hr_expense/analytic_contract_hr_expense.py @@ -19,7 +19,6 @@ # ############################################################################## from openerp.osv import fields, osv -from openerp.osv.orm import intersect from openerp.tools.translate import _ from openerp.addons.decimal_precision import decimal_precision as dp @@ -128,7 +127,7 @@ class account_analytic_account(osv.osv): act_obj = self.pool.get('ir.actions.act_window') dummy, act_window_id = mod_obj.get_object_reference(cr, uid, 'hr_expense', 'expense_all') - result = act_obj.read(cr, uid, act_window_id, context=context) + result = act_obj.read(cr, uid, [act_window_id], context=context)[0] line_ids = self.pool.get('hr.expense.line').search(cr,uid,[('analytic_account', 'in', ids)]) result['domain'] = [('line_ids', 'in', line_ids)] diff --git a/addons/analytic_user_function/analytic_user_function.py b/addons/analytic_user_function/analytic_user_function.py index 5a17ac91e53..42530bfbb7b 100644 --- a/addons/analytic_user_function/analytic_user_function.py +++ b/addons/analytic_user_function/analytic_user_function.py @@ -59,7 +59,7 @@ class analytic_user_funct_grid(osv.osv): class account_analytic_account(osv.osv): _inherit = "account.analytic.account" _columns = { - 'user_product_ids': fields.one2many('analytic.user.funct.grid', 'account_id', 'Users/Products Rel.'), + 'user_product_ids': fields.one2many('analytic.user.funct.grid', 'account_id', 'Users/Products Rel.', copy=True), } diff --git a/addons/auth_crypt/auth_crypt.py b/addons/auth_crypt/auth_crypt.py index 6c9deb51e92..384e7960cbe 100644 --- a/addons/auth_crypt/auth_crypt.py +++ b/addons/auth_crypt/auth_crypt.py @@ -32,6 +32,7 @@ class res_users(osv.osv): def set_pw(self, cr, uid, id, name, value, args, context): if value: self._set_password(cr, uid, id, value, context=context) + self.invalidate_cache(cr, uid, context=context) def get_pw( self, cr, uid, ids, name, args, context ): cr.execute('select id, password from res_users where id in %s', (tuple(map(int, ids)),)) @@ -39,7 +40,7 @@ class res_users(osv.osv): _columns = { 'password': fields.function(get_pw, fnct_inv=set_pw, type='char', string='Password', invisible=True, store=True), - 'password_crypt': fields.char(string='Encrypted Password', invisible=True), + 'password_crypt': fields.char(string='Encrypted Password', invisible=True, copy=False), } def check_credentials(self, cr, uid, password): @@ -50,6 +51,7 @@ class res_users(osv.osv): stored, encrypted = cr.fetchone() if stored and not encrypted: self._set_password(cr, uid, uid, stored) + self.invalidate_cache(cr, uid) try: return super(res_users, self).check_credentials(cr, uid, password) except openerp.exceptions.AccessDenied: diff --git a/addons/auth_crypt/i18n/base_crypt.pot b/addons/auth_crypt/i18n/base_crypt.pot deleted file mode 100644 index 4aa6bbcdc54..00000000000 --- a/addons/auth_crypt/i18n/base_crypt.pot +++ /dev/null @@ -1,22 +0,0 @@ -# Translation of OpenERP Server. -# This file contains the translation of the following modules: -# * base_crypt -# -msgid "" -msgstr "" -"Project-Id-Version: OpenERP Server 7.0alpha\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2012-12-03 16:03+0000\n" -"PO-Revision-Date: 2012-12-03 16:03+0000\n" -"Last-Translator: <>\n" -"Language-Team: \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: \n" -"Plural-Forms: \n" - -#. module: base_crypt -#: model:ir.model,name:base_crypt.model_res_users -msgid "Users" -msgstr "" - diff --git a/addons/auth_crypt/i18n/bg.po b/addons/auth_crypt/i18n/bg.po deleted file mode 100644 index 24e74238284..00000000000 --- a/addons/auth_crypt/i18n/bg.po +++ /dev/null @@ -1,76 +0,0 @@ -# Bulgarian translation for openobject-addons -# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2011. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2012-12-03 16:03+0000\n" -"PO-Revision-Date: 2011-02-18 09:47+0000\n" -"Last-Translator: FULL NAME \n" -"Language-Team: Bulgarian \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-12-04 05:53+0000\n" -"X-Generator: Launchpad (build 16335)\n" - -#. module: base_crypt -#: model:ir.model,name:base_crypt.model_res_users -msgid "Users" -msgstr "" - -#~ msgid "" -#~ "This module replaces the cleartext password in the database with a password " -#~ "hash,\n" -#~ "preventing anyone from reading the original password.\n" -#~ "For your existing user base, the removal of the cleartext passwords occurs " -#~ "the first time\n" -#~ "a user logs into the database, after installing base_crypt.\n" -#~ "After installing this module it won't be possible to recover a forgotten " -#~ "password for your\n" -#~ "users, the only solution is for an admin to set a new password.\n" -#~ "\n" -#~ "Note: installing this module does not mean you can ignore basic security " -#~ "measures,\n" -#~ "as the password is still transmitted unencrypted on the network (by the " -#~ "client),\n" -#~ "unless you are using a secure protocol such as XML-RPCS.\n" -#~ " " -#~ msgstr "" -#~ "Този модул заменя паролите в чист текст в базата данни с хеширани такива,\n" -#~ " за предотвратяване прочита на оригиналната парола.\n" -#~ " За съществуващата потребителска база, когато премахването на паролите в " -#~ "чист текст се случва за първи път,\n" -#~ " влизане на потребител става, след инсталиране на base_crypt.\n" -#~ " След като инсталирате този модул няма да бъде възможно да се възстанови " -#~ "забравена парола за\n" -#~ " потребители, единственото решение е администратор, да зададе нова парола.\n" -#~ "\n" -#~ " Забележка: инсталиране на този модул не значи, че може да пренебрегне " -#~ "основните мерки за сигурност,\n" -#~ " като парола все още е изпратена в прав текст в мрежата (от клиента),\n" -#~ " освен ако не използвате защитен протокол, като XML-RPCS.\n" -#~ " " - -#, python-format -#~ msgid "Error" -#~ msgstr "Грешка" - -#~ msgid "Base - Password Encryption" -#~ msgstr "База - Криптиране на пароли" - -#, python-format -#~ msgid "Please specify the password !" -#~ msgstr "Моля изберете парола!" - -#~ msgid "The chosen company is not in the allowed companies for this user" -#~ msgstr "Избраната фирма не е измежду разрешените фирми за този потребител" - -#~ msgid "res.users" -#~ msgstr "res.users" - -#~ msgid "You can not have two users with the same login !" -#~ msgstr "Не може да има двама потребители с един и същ \"логин\"!" diff --git a/addons/auth_crypt/i18n/ca.po b/addons/auth_crypt/i18n/ca.po deleted file mode 100644 index 2d08f1ab474..00000000000 --- a/addons/auth_crypt/i18n/ca.po +++ /dev/null @@ -1,78 +0,0 @@ -# Catalan translation for openobject-addons -# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2011. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2012-12-03 16:03+0000\n" -"PO-Revision-Date: 2011-03-26 18:08+0000\n" -"Last-Translator: FULL NAME \n" -"Language-Team: Catalan \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-12-04 05:53+0000\n" -"X-Generator: Launchpad (build 16335)\n" - -#. module: base_crypt -#: model:ir.model,name:base_crypt.model_res_users -msgid "Users" -msgstr "" - -#~ msgid "" -#~ "This module replaces the cleartext password in the database with a password " -#~ "hash,\n" -#~ "preventing anyone from reading the original password.\n" -#~ "For your existing user base, the removal of the cleartext passwords occurs " -#~ "the first time\n" -#~ "a user logs into the database, after installing base_crypt.\n" -#~ "After installing this module it won't be possible to recover a forgotten " -#~ "password for your\n" -#~ "users, the only solution is for an admin to set a new password.\n" -#~ "\n" -#~ "Note: installing this module does not mean you can ignore basic security " -#~ "measures,\n" -#~ "as the password is still transmitted unencrypted on the network (by the " -#~ "client),\n" -#~ "unless you are using a secure protocol such as XML-RPCS.\n" -#~ " " -#~ msgstr "" -#~ "Aquest mòdul substitueix la contrasenya en text pla per un hash codificat,\n" -#~ "prevenint que algú pugui llegir la contrasenya original.\n" -#~ "Per a un usuari existent, l'esborrat de la contrasenya en text pla es " -#~ "realitza la primera\n" -#~ "vegada que l'usuari es connecta després d'instal·lar base_crypt.\n" -#~ "Després d'instal·lar aquest mòdul, els usuaris no podran recuperar la seva " -#~ "contrasenya,\n" -#~ "un administrador haurà d'introduir una nova contrasenya.\n" -#~ "\n" -#~ "Nota: Instal·lar aquest mòdul no significa que podeu ignorar les mesures " -#~ "bàsiques de seguretat,\n" -#~ "perquè la contrasenya és enviada sense codificar pel client,\n" -#~ "a menys que utilitzeu un protocol segur com XML-RPCS.\n" -#~ " " - -#, python-format -#~ msgid "Please specify the password !" -#~ msgstr "Si us plau, escriviu una contrasenya!" - -#~ msgid "The chosen company is not in the allowed companies for this user" -#~ msgstr "" -#~ "La companyia seleccionada no està en les companyies permeses per aquest " -#~ "usuari" - -#~ msgid "res.users" -#~ msgstr "res.usuaris" - -#, python-format -#~ msgid "Error" -#~ msgstr "Error" - -#~ msgid "Base - Password Encryption" -#~ msgstr "Base - Encriptació de la Contrasenya" - -#~ msgid "You can not have two users with the same login !" -#~ msgstr "No podeu tenir dos usuaris amb el mateix identificador d'usuari!" diff --git a/addons/auth_crypt/i18n/el.po b/addons/auth_crypt/i18n/el.po deleted file mode 100644 index 4adc016c3e1..00000000000 --- a/addons/auth_crypt/i18n/el.po +++ /dev/null @@ -1,24 +0,0 @@ -# Translation of OpenERP Server. -# This file contains the translation of the following modules: -# * base_crypt -# -# Copyright (C) 2008,2009 P. Christeas -# <> <>, 2009. -msgid "" -msgstr "" -"Project-Id-Version: OpenERP Server 5.0.0\n" -"Report-Msgid-Bugs-To: support@openerp.com\n" -"POT-Creation-Date: 2012-12-03 16:03+0000\n" -"PO-Revision-Date: 2011-02-15 15:37+0000\n" -"Last-Translator: <> <>\n" -"Language-Team: <>\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-12-04 05:53+0000\n" -"X-Generator: Launchpad (build 16335)\n" - -#. module: base_crypt -#: model:ir.model,name:base_crypt.model_res_users -msgid "Users" -msgstr "" diff --git a/addons/auth_crypt/i18n/es_CL.po b/addons/auth_crypt/i18n/es_CL.po deleted file mode 100644 index 3f53b8b8b88..00000000000 --- a/addons/auth_crypt/i18n/es_CL.po +++ /dev/null @@ -1,80 +0,0 @@ -# Spanish (Chile) translation for openobject-addons -# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2011. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2012-12-03 16:03+0000\n" -"PO-Revision-Date: 2011-10-03 17:17+0000\n" -"Last-Translator: David Acevedo Toledo \n" -"Language-Team: Spanish (Chile) \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-12-04 05:53+0000\n" -"X-Generator: Launchpad (build 16335)\n" - -#. module: base_crypt -#: model:ir.model,name:base_crypt.model_res_users -msgid "Users" -msgstr "" - -#, python-format -#~ msgid "Error" -#~ msgstr "Error!" - -#~ msgid "res.users" -#~ msgstr "res.users" - -#~ msgid "Base - Password Encryption" -#~ msgstr "Base - Encriptación de la Contraseña" - -#, python-format -#~ msgid "Please specify the password !" -#~ msgstr "¡Por favor, escriba una contraseña!" - -#~ msgid "The chosen company is not in the allowed companies for this user" -#~ msgstr "" -#~ "La compañía seleccionada no está dentro de las compañías autorizadas para " -#~ "este usuario" - -#~ msgid "You can not have two users with the same login !" -#~ msgstr "¡No puede tener dos usuarios con el mismo nombre!" - -#~ msgid "" -#~ "This module replaces the cleartext password in the database with a password " -#~ "hash,\n" -#~ "preventing anyone from reading the original password.\n" -#~ "For your existing user base, the removal of the cleartext passwords occurs " -#~ "the first time\n" -#~ "a user logs into the database, after installing base_crypt.\n" -#~ "After installing this module it won't be possible to recover a forgotten " -#~ "password for your\n" -#~ "users, the only solution is for an admin to set a new password.\n" -#~ "\n" -#~ "Note: installing this module does not mean you can ignore basic security " -#~ "measures,\n" -#~ "as the password is still transmitted unencrypted on the network (by the " -#~ "client),\n" -#~ "unless you are using a secure protocol such as XML-RPCS.\n" -#~ " " -#~ msgstr "" -#~ "Este módulo sustituye la contraseña escrita en texto plano por una " -#~ "codificada,\n" -#~ "previniendo que alguien pueda leer la contraseña original.\n" -#~ "Para un usuario existente, el sustitución de la contraseña en texto plano se " -#~ "realiza la primera vez\n" -#~ "que el usuario se conecte después de instalar base_crypt.\n" -#~ "Después de instalar este módulo los usuarios no podrán recuperar su " -#~ "contraseña olvidada,\n" -#~ "un administrador tendrá que cambiarla por una nueva.\n" -#~ "\n" -#~ "Nota: instalar este módulo no significa que pueda ignorar las medidas " -#~ "básicas de seguridad,\n" -#~ "como la contraseña que es enviada por el cliente que sigue sin ser " -#~ "codificada en la red,\n" -#~ "a menos que utilice un protocolo seguro como XML-RPCS.\n" -#~ " " diff --git a/addons/auth_crypt/i18n/es_CR.po b/addons/auth_crypt/i18n/es_CR.po deleted file mode 100644 index 35f8372f4c7..00000000000 --- a/addons/auth_crypt/i18n/es_CR.po +++ /dev/null @@ -1,79 +0,0 @@ -# Spanish translation for openobject-addons -# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2011. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2012-12-03 16:03+0000\n" -"PO-Revision-Date: 2012-02-13 19:04+0000\n" -"Last-Translator: Carlos Vásquez (CLEARCORP) " -"\n" -"Language-Team: Spanish \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-12-04 05:53+0000\n" -"X-Generator: Launchpad (build 16335)\n" -"Language: es\n" - -#. module: base_crypt -#: model:ir.model,name:base_crypt.model_res_users -msgid "Users" -msgstr "" - -#~ msgid "res.users" -#~ msgstr "res.usuarios" - -#~ msgid "You can not have two users with the same login !" -#~ msgstr "¡No puede tener dos usuarios con el mismo identificador de usuario!" - -#~ msgid "The chosen company is not in the allowed companies for this user" -#~ msgstr "" -#~ "La compañía seleccionada no está autorizada como compañía para este usuario" - -#, python-format -#~ msgid "Please specify the password !" -#~ msgstr "¡Por favor, escriba una contraseña!" - -#, python-format -#~ msgid "Error" -#~ msgstr "Error" - -#~ msgid "" -#~ "This module replaces the cleartext password in the database with a password " -#~ "hash,\n" -#~ "preventing anyone from reading the original password.\n" -#~ "For your existing user base, the removal of the cleartext passwords occurs " -#~ "the first time\n" -#~ "a user logs into the database, after installing base_crypt.\n" -#~ "After installing this module it won't be possible to recover a forgotten " -#~ "password for your\n" -#~ "users, the only solution is for an admin to set a new password.\n" -#~ "\n" -#~ "Note: installing this module does not mean you can ignore basic security " -#~ "measures,\n" -#~ "as the password is still transmitted unencrypted on the network (by the " -#~ "client),\n" -#~ "unless you are using a secure protocol such as XML-RPCS.\n" -#~ " " -#~ msgstr "" -#~ "Este módulo sustituye la contraseña en texto plano por un hash codificado,\n" -#~ "previniendo que alguien pueda leer la contraseña original.\n" -#~ "Para un usuario existente, el borrado de la contraseña en texto plano se " -#~ "realiza la primera vez\n" -#~ "que el usuario se conecte después de instalar base_crypt.\n" -#~ "Después de instalar este módulo los usuarios no podrán recuperar su " -#~ "contraseña,\n" -#~ "un administrador tendrá que introducir una nueva contraseña.\n" -#~ "\n" -#~ "Nota: instalar este módulo no significa que pueda ignorar las medidas " -#~ "básicas de seguridad,\n" -#~ "porque la contraseña es enviada sin codificar por el cliente,\n" -#~ "a menos que utilice un protocolo seguro como XML-RPCS.\n" -#~ " " - -#~ msgid "Base - Password Encryption" -#~ msgstr "Base - Encriptación de la Contraseña" diff --git a/addons/auth_crypt/i18n/es_MX.po b/addons/auth_crypt/i18n/es_MX.po deleted file mode 100644 index be7c1499288..00000000000 --- a/addons/auth_crypt/i18n/es_MX.po +++ /dev/null @@ -1,86 +0,0 @@ -# Spanish translation for openobject-addons -# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2011. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2011-01-11 11:14+0000\n" -"PO-Revision-Date: 2011-02-15 15:37+0000\n" -"Last-Translator: FULL NAME \n" -"Language-Team: Spanish \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2011-09-05 05:58+0000\n" -"X-Generator: Launchpad (build 13830)\n" - -#. module: base_crypt -#: sql_constraint:res.users:0 -msgid "You can not have two users with the same login !" -msgstr "¡No puede tener dos usuarios con el mismo identificador de usuario!" - -#. module: base_crypt -#: model:ir.model,name:base_crypt.model_res_users -msgid "res.users" -msgstr "res.usuarios" - -#. module: base_crypt -#: constraint:res.users:0 -msgid "The chosen company is not in the allowed companies for this user" -msgstr "" -"La compañía seleccionada no está autorizada como compañía para este usuario" - -#. module: base_crypt -#: code:addons/base_crypt/crypt.py:132 -#, python-format -msgid "Please specify the password !" -msgstr "¡Por favor, escriba una contraseña!" - -#. module: base_crypt -#: model:ir.module.module,shortdesc:base_crypt.module_meta_information -msgid "Base - Password Encryption" -msgstr "Base - Encriptación de la Contraseña" - -#. module: base_crypt -#: code:addons/base_crypt/crypt.py:132 -#, python-format -msgid "Error" -msgstr "Error" - -#. module: base_crypt -#: model:ir.module.module,description:base_crypt.module_meta_information -msgid "" -"This module replaces the cleartext password in the database with a password " -"hash,\n" -"preventing anyone from reading the original password.\n" -"For your existing user base, the removal of the cleartext passwords occurs " -"the first time\n" -"a user logs into the database, after installing base_crypt.\n" -"After installing this module it won't be possible to recover a forgotten " -"password for your\n" -"users, the only solution is for an admin to set a new password.\n" -"\n" -"Note: installing this module does not mean you can ignore basic security " -"measures,\n" -"as the password is still transmitted unencrypted on the network (by the " -"client),\n" -"unless you are using a secure protocol such as XML-RPCS.\n" -" " -msgstr "" -"Este módulo sustituye la contraseña en texto plano por un hash codificado,\n" -"previniendo que alguien pueda leer la contraseña original.\n" -"Para un usuario existente, el borrado de la contraseña en texto plano se " -"realiza la primera vez\n" -"que el usuario se conecte después de instalar base_crypt.\n" -"Después de instalar este módulo los usuarios no podrán recuperar su " -"contraseña,\n" -"un administrador tendrá que introducir una nueva contraseña.\n" -"\n" -"Nota: instalar este módulo no significa que pueda ignorar las medidas " -"básicas de seguridad,\n" -"porque la contraseña es enviada sin codificar por el cliente,\n" -"a menos que utilice un protocolo seguro como XML-RPCS.\n" -" " diff --git a/addons/auth_crypt/i18n/es_PY.po b/addons/auth_crypt/i18n/es_PY.po deleted file mode 100644 index 8e5cbcbee86..00000000000 --- a/addons/auth_crypt/i18n/es_PY.po +++ /dev/null @@ -1,78 +0,0 @@ -# Spanish (Paraguay) translation for openobject-addons -# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2011. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2012-12-03 16:03+0000\n" -"PO-Revision-Date: 2011-03-08 17:36+0000\n" -"Last-Translator: FULL NAME \n" -"Language-Team: Spanish (Paraguay) \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-12-04 05:53+0000\n" -"X-Generator: Launchpad (build 16335)\n" - -#. module: base_crypt -#: model:ir.model,name:base_crypt.model_res_users -msgid "Users" -msgstr "" - -#~ msgid "" -#~ "This module replaces the cleartext password in the database with a password " -#~ "hash,\n" -#~ "preventing anyone from reading the original password.\n" -#~ "For your existing user base, the removal of the cleartext passwords occurs " -#~ "the first time\n" -#~ "a user logs into the database, after installing base_crypt.\n" -#~ "After installing this module it won't be possible to recover a forgotten " -#~ "password for your\n" -#~ "users, the only solution is for an admin to set a new password.\n" -#~ "\n" -#~ "Note: installing this module does not mean you can ignore basic security " -#~ "measures,\n" -#~ "as the password is still transmitted unencrypted on the network (by the " -#~ "client),\n" -#~ "unless you are using a secure protocol such as XML-RPCS.\n" -#~ " " -#~ msgstr "" -#~ "Este módulo sustituye la contraseña en texto plano por un hash codificado,\n" -#~ "previniendo que alguien pueda leer la contraseña original.\n" -#~ "Para un usuario existente, el borrado de la contraseña en texto plano se " -#~ "realiza la primera vez\n" -#~ "que el usuario se conecte después de instalar base_crypt.\n" -#~ "Después de instalar este módulo los usuarios no podrán recuperar su " -#~ "contraseña,\n" -#~ "un administrador tendrá que introducir una nueva contraseña.\n" -#~ "\n" -#~ "Nota: instalar este módulo no significa que pueda ignorar las medidas " -#~ "básicas de seguridad,\n" -#~ "porque la contraseña es enviada sin codificar por el cliente,\n" -#~ "a menos que utilice un protocolo seguro como XML-RPCS.\n" -#~ " " - -#, python-format -#~ msgid "Error" -#~ msgstr "Error!" - -#~ msgid "Base - Password Encryption" -#~ msgstr "Base - Encriptación de la Contraseña" - -#, python-format -#~ msgid "Please specify the password !" -#~ msgstr "¡Por favor, escriba una contraseña!" - -#~ msgid "The chosen company is not in the allowed companies for this user" -#~ msgstr "" -#~ "La compañía seleccionada no está en las compañías permitidas para este " -#~ "usuario" - -#~ msgid "res.users" -#~ msgstr "res.users" - -#~ msgid "You can not have two users with the same login !" -#~ msgstr "¡No puede tener dos usuarios con el mismo identificador de usuario!" diff --git a/addons/auth_crypt/i18n/es_VE.po b/addons/auth_crypt/i18n/es_VE.po deleted file mode 100644 index be7c1499288..00000000000 --- a/addons/auth_crypt/i18n/es_VE.po +++ /dev/null @@ -1,86 +0,0 @@ -# Spanish translation for openobject-addons -# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2011. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2011-01-11 11:14+0000\n" -"PO-Revision-Date: 2011-02-15 15:37+0000\n" -"Last-Translator: FULL NAME \n" -"Language-Team: Spanish \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2011-09-05 05:58+0000\n" -"X-Generator: Launchpad (build 13830)\n" - -#. module: base_crypt -#: sql_constraint:res.users:0 -msgid "You can not have two users with the same login !" -msgstr "¡No puede tener dos usuarios con el mismo identificador de usuario!" - -#. module: base_crypt -#: model:ir.model,name:base_crypt.model_res_users -msgid "res.users" -msgstr "res.usuarios" - -#. module: base_crypt -#: constraint:res.users:0 -msgid "The chosen company is not in the allowed companies for this user" -msgstr "" -"La compañía seleccionada no está autorizada como compañía para este usuario" - -#. module: base_crypt -#: code:addons/base_crypt/crypt.py:132 -#, python-format -msgid "Please specify the password !" -msgstr "¡Por favor, escriba una contraseña!" - -#. module: base_crypt -#: model:ir.module.module,shortdesc:base_crypt.module_meta_information -msgid "Base - Password Encryption" -msgstr "Base - Encriptación de la Contraseña" - -#. module: base_crypt -#: code:addons/base_crypt/crypt.py:132 -#, python-format -msgid "Error" -msgstr "Error" - -#. module: base_crypt -#: model:ir.module.module,description:base_crypt.module_meta_information -msgid "" -"This module replaces the cleartext password in the database with a password " -"hash,\n" -"preventing anyone from reading the original password.\n" -"For your existing user base, the removal of the cleartext passwords occurs " -"the first time\n" -"a user logs into the database, after installing base_crypt.\n" -"After installing this module it won't be possible to recover a forgotten " -"password for your\n" -"users, the only solution is for an admin to set a new password.\n" -"\n" -"Note: installing this module does not mean you can ignore basic security " -"measures,\n" -"as the password is still transmitted unencrypted on the network (by the " -"client),\n" -"unless you are using a secure protocol such as XML-RPCS.\n" -" " -msgstr "" -"Este módulo sustituye la contraseña en texto plano por un hash codificado,\n" -"previniendo que alguien pueda leer la contraseña original.\n" -"Para un usuario existente, el borrado de la contraseña en texto plano se " -"realiza la primera vez\n" -"que el usuario se conecte después de instalar base_crypt.\n" -"Después de instalar este módulo los usuarios no podrán recuperar su " -"contraseña,\n" -"un administrador tendrá que introducir una nueva contraseña.\n" -"\n" -"Nota: instalar este módulo no significa que pueda ignorar las medidas " -"básicas de seguridad,\n" -"porque la contraseña es enviada sin codificar por el cliente,\n" -"a menos que utilice un protocolo seguro como XML-RPCS.\n" -" " diff --git a/addons/auth_crypt/i18n/fa.po b/addons/auth_crypt/i18n/fa.po deleted file mode 100644 index e4291629d6f..00000000000 --- a/addons/auth_crypt/i18n/fa.po +++ /dev/null @@ -1,23 +0,0 @@ -# Persian translation for openobject-addons -# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2011. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2012-12-03 16:03+0000\n" -"PO-Revision-Date: 2011-12-18 20:12+0000\n" -"Last-Translator: FULL NAME \n" -"Language-Team: Persian \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-12-04 05:53+0000\n" -"X-Generator: Launchpad (build 16335)\n" - -#. module: base_crypt -#: model:ir.model,name:base_crypt.model_res_users -msgid "Users" -msgstr "" diff --git a/addons/auth_crypt/i18n/fi.po b/addons/auth_crypt/i18n/fi.po deleted file mode 100644 index 39221ce5d69..00000000000 --- a/addons/auth_crypt/i18n/fi.po +++ /dev/null @@ -1,40 +0,0 @@ -# Finnish translation for openobject-addons -# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2011. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2012-12-03 16:03+0000\n" -"PO-Revision-Date: 2011-06-08 10:57+0000\n" -"Last-Translator: FULL NAME \n" -"Language-Team: Finnish \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-12-04 05:53+0000\n" -"X-Generator: Launchpad (build 16335)\n" - -#. module: base_crypt -#: model:ir.model,name:base_crypt.model_res_users -msgid "Users" -msgstr "" - -#, python-format -#~ msgid "Error" -#~ msgstr "Virhe" - -#~ msgid "Base - Password Encryption" -#~ msgstr "Base - Salasanan kryptaus" - -#, python-format -#~ msgid "Please specify the password !" -#~ msgstr "Määrittele salasana !" - -#~ msgid "The chosen company is not in the allowed companies for this user" -#~ msgstr "Valittu yritys ei ole sallittu tälle käyttäjälle" - -#~ msgid "You can not have two users with the same login !" -#~ msgstr "Kahdella eri käyttäjällä ei voi olla samaa käyttäjätunnusta!" diff --git a/addons/auth_crypt/i18n/gu.po b/addons/auth_crypt/i18n/gu.po deleted file mode 100644 index 6d506cb3573..00000000000 --- a/addons/auth_crypt/i18n/gu.po +++ /dev/null @@ -1,37 +0,0 @@ -# Gujarati translation for openobject-addons -# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2012. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2012-12-03 16:03+0000\n" -"PO-Revision-Date: 2012-04-19 08:51+0000\n" -"Last-Translator: FULL NAME \n" -"Language-Team: Gujarati \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-12-04 05:53+0000\n" -"X-Generator: Launchpad (build 16335)\n" - -#. module: base_crypt -#: model:ir.model,name:base_crypt.model_res_users -msgid "Users" -msgstr "" - -#~ msgid "You can not have two users with the same login !" -#~ msgstr "તમે બે વપરાશકર્તાઓને એક જ લોગીન ન કરી શકો!" - -#, python-format -#~ msgid "Please specify the password !" -#~ msgstr "પાસવર્ડ સ્પષ્ટ કરો!" - -#~ msgid "The chosen company is not in the allowed companies for this user" -#~ msgstr "પસંદ કરેલ કંપની માન્ય કંપનીઓમાં આ વપરાશકર્તા માટે નથી" - -#, python-format -#~ msgid "Error" -#~ msgstr "ભૂલ" diff --git a/addons/auth_crypt/i18n/id.po b/addons/auth_crypt/i18n/id.po deleted file mode 100644 index 035480b7a84..00000000000 --- a/addons/auth_crypt/i18n/id.po +++ /dev/null @@ -1,23 +0,0 @@ -# Indonesian translation for openobject-addons -# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2012. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2012-12-03 16:03+0000\n" -"PO-Revision-Date: 2012-12-09 08:46+0000\n" -"Last-Translator: Budhi Hartono \n" -"Language-Team: Indonesian \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-12-10 04:39+0000\n" -"X-Generator: Launchpad (build 16341)\n" - -#. module: base_crypt -#: model:ir.model,name:base_crypt.model_res_users -msgid "Users" -msgstr "Pengguna" diff --git a/addons/auth_crypt/i18n/ja.po b/addons/auth_crypt/i18n/ja.po deleted file mode 100644 index d92df144a2c..00000000000 --- a/addons/auth_crypt/i18n/ja.po +++ /dev/null @@ -1,40 +0,0 @@ -# Japanese translation for openobject-addons -# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2012. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2012-12-03 16:03+0000\n" -"PO-Revision-Date: 2012-04-01 06:05+0000\n" -"Last-Translator: Masaki Yamaya \n" -"Language-Team: Japanese \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-12-04 05:53+0000\n" -"X-Generator: Launchpad (build 16335)\n" - -#. module: base_crypt -#: model:ir.model,name:base_crypt.model_res_users -msgid "Users" -msgstr "" - -#~ msgid "You can not have two users with the same login !" -#~ msgstr "同一のログインに2つのユーザを持つことはできません!" - -#, python-format -#~ msgid "Error" -#~ msgstr "エラー" - -#~ msgid "The chosen company is not in the allowed companies for this user" -#~ msgstr "選択した会社は、このユーザに許された会社ではありません。" - -#~ msgid "res.users" -#~ msgstr "res.users" - -#, python-format -#~ msgid "Please specify the password !" -#~ msgstr "パスワードを指定してください!" diff --git a/addons/auth_crypt/i18n/lv.po b/addons/auth_crypt/i18n/lv.po deleted file mode 100644 index 4d34fec70a9..00000000000 --- a/addons/auth_crypt/i18n/lv.po +++ /dev/null @@ -1,40 +0,0 @@ -# Latvian translation for openobject-addons -# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2012. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2012-12-03 16:03+0000\n" -"PO-Revision-Date: 2012-10-16 16:11+0000\n" -"Last-Translator: FULL NAME \n" -"Language-Team: Latvian \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-12-04 05:53+0000\n" -"X-Generator: Launchpad (build 16335)\n" - -#. module: base_crypt -#: model:ir.model,name:base_crypt.model_res_users -msgid "Users" -msgstr "" - -#~ msgid "You can not have two users with the same login !" -#~ msgstr "Nevar būt divi lietotāji ar vienādu pieteikuma vārdu!" - -#, python-format -#~ msgid "Error" -#~ msgstr "Kļūda" - -#~ msgid "The chosen company is not in the allowed companies for this user" -#~ msgstr "Izvēlētais uzņēmums nav šim lietotājam atļauto uzņēmumu sarakstā" - -#~ msgid "res.users" -#~ msgstr "res.users" - -#, python-format -#~ msgid "Please specify the password !" -#~ msgstr "Lūdzu norādiet paroli!" diff --git a/addons/auth_crypt/i18n/nb.po b/addons/auth_crypt/i18n/nb.po deleted file mode 100644 index c9e2d8709d1..00000000000 --- a/addons/auth_crypt/i18n/nb.po +++ /dev/null @@ -1,44 +0,0 @@ -# Norwegian Bokmal translation for openobject-addons -# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2011. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2012-12-03 16:03+0000\n" -"PO-Revision-Date: 2012-12-04 20:38+0000\n" -"Last-Translator: Kaare Pettersen \n" -"Language-Team: Norwegian Bokmal \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-12-05 05:20+0000\n" -"X-Generator: Launchpad (build 16335)\n" - -#. module: base_crypt -#: model:ir.model,name:base_crypt.model_res_users -msgid "Users" -msgstr "Brukere." - -#, python-format -#~ msgid "Error" -#~ msgstr "Feil" - -#~ msgid "Base - Password Encryption" -#~ msgstr "Basis - Passord kryptering" - -#, python-format -#~ msgid "Please specify the password !" -#~ msgstr "Vennligst angi passordet !" - -#~ msgid "The chosen company is not in the allowed companies for this user" -#~ msgstr "" -#~ "Det valgte firmaet er ikke i listen over tillatte firmaer for denne brukeren" - -#~ msgid "res.users" -#~ msgstr "res.users" - -#~ msgid "You can not have two users with the same login !" -#~ msgstr "Du kan ikke ha to brukere med samme login !" diff --git a/addons/auth_crypt/i18n/oc.po b/addons/auth_crypt/i18n/oc.po deleted file mode 100644 index a6cd3aa45a1..00000000000 --- a/addons/auth_crypt/i18n/oc.po +++ /dev/null @@ -1,45 +0,0 @@ -# Occitan (post 1500) translation for openobject-addons -# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2011. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2012-12-03 16:03+0000\n" -"PO-Revision-Date: 2011-11-20 09:17+0000\n" -"Last-Translator: FULL NAME \n" -"Language-Team: Occitan (post 1500) \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-12-04 05:53+0000\n" -"X-Generator: Launchpad (build 16335)\n" - -#. module: base_crypt -#: model:ir.model,name:base_crypt.model_res_users -msgid "Users" -msgstr "" - -#, python-format -#~ msgid "Error" -#~ msgstr "Error" - -#~ msgid "Base - Password Encryption" -#~ msgstr "Basa - Chifratge del senhal" - -#, python-format -#~ msgid "Please specify the password !" -#~ msgstr "Entratz un senhal" - -#~ msgid "The chosen company is not in the allowed companies for this user" -#~ msgstr "" -#~ "La societat causida fa pas partida de las societats autorizadas per aqueste " -#~ "utilizaire" - -#~ msgid "res.users" -#~ msgstr "res.users" - -#~ msgid "You can not have two users with the same login !" -#~ msgstr "Podètz pas aver dos utilizaires amb lo meteis identificant !" diff --git a/addons/auth_crypt/i18n/sk.po b/addons/auth_crypt/i18n/sk.po deleted file mode 100644 index b082277e875..00000000000 --- a/addons/auth_crypt/i18n/sk.po +++ /dev/null @@ -1,41 +0,0 @@ -# Slovak translation for openobject-addons -# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2011. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2012-12-03 16:03+0000\n" -"PO-Revision-Date: 2011-02-21 08:14+0000\n" -"Last-Translator: FULL NAME \n" -"Language-Team: Slovak \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-12-04 05:53+0000\n" -"X-Generator: Launchpad (build 16335)\n" - -#. module: base_crypt -#: model:ir.model,name:base_crypt.model_res_users -msgid "Users" -msgstr "" - -#, python-format -#~ msgid "Error" -#~ msgstr "Chyba" - -#, python-format -#~ msgid "Please specify the password !" -#~ msgstr "Prosím, zadajte heslo!" - -#~ msgid "The chosen company is not in the allowed companies for this user" -#~ msgstr "" -#~ "Vybraná spoločnosť nie je medzi schválenými spoločnosťami tohto používateľa" - -#~ msgid "res.users" -#~ msgstr "res.users" - -#~ msgid "You can not have two users with the same login !" -#~ msgstr "Nemôžte mať dvoch používateľov s rovnakým pristúpovým menom!" diff --git a/addons/auth_crypt/i18n/sq.po b/addons/auth_crypt/i18n/sq.po deleted file mode 100644 index c57af2d287b..00000000000 --- a/addons/auth_crypt/i18n/sq.po +++ /dev/null @@ -1,23 +0,0 @@ -# Albanian translation for openobject-addons -# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2011. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2012-12-03 16:03+0000\n" -"PO-Revision-Date: 2011-03-28 15:26+0000\n" -"Last-Translator: FULL NAME \n" -"Language-Team: Albanian \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-12-04 05:53+0000\n" -"X-Generator: Launchpad (build 16335)\n" - -#. module: base_crypt -#: model:ir.model,name:base_crypt.model_res_users -msgid "Users" -msgstr "" diff --git a/addons/auth_crypt/i18n/sr@latin.po b/addons/auth_crypt/i18n/sr@latin.po deleted file mode 100644 index 9e9547f154b..00000000000 --- a/addons/auth_crypt/i18n/sr@latin.po +++ /dev/null @@ -1,76 +0,0 @@ -# Serbian Latin translation for openobject-addons -# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2011. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2012-12-03 16:03+0000\n" -"PO-Revision-Date: 2011-10-05 13:25+0000\n" -"Last-Translator: Milan Milosevic \n" -"Language-Team: Serbian Latin \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-12-04 05:53+0000\n" -"X-Generator: Launchpad (build 16335)\n" - -#. module: base_crypt -#: model:ir.model,name:base_crypt.model_res_users -msgid "Users" -msgstr "" - -#, python-format -#~ msgid "Error" -#~ msgstr "Greška" - -#~ msgid "Base - Password Encryption" -#~ msgstr "Baza - Šifrovanje lozinke" - -#, python-format -#~ msgid "Please specify the password !" -#~ msgstr "Molimo odredite lozinku" - -#~ msgid "res.users" -#~ msgstr "res.users" - -#~ msgid "You can not have two users with the same login !" -#~ msgstr "Ne možete imati dva korisnika sa istom prijavom!" - -#~ msgid "" -#~ "This module replaces the cleartext password in the database with a password " -#~ "hash,\n" -#~ "preventing anyone from reading the original password.\n" -#~ "For your existing user base, the removal of the cleartext passwords occurs " -#~ "the first time\n" -#~ "a user logs into the database, after installing base_crypt.\n" -#~ "After installing this module it won't be possible to recover a forgotten " -#~ "password for your\n" -#~ "users, the only solution is for an admin to set a new password.\n" -#~ "\n" -#~ "Note: installing this module does not mean you can ignore basic security " -#~ "measures,\n" -#~ "as the password is still transmitted unencrypted on the network (by the " -#~ "client),\n" -#~ "unless you are using a secure protocol such as XML-RPCS.\n" -#~ " " -#~ msgstr "" -#~ "Ovaj modul zamenjuje vidljivu lozinku u bazi podataka znacima,\n" -#~ "što onemogućava bilo koga da pročita originalnu lozinku.\n" -#~ "Što se tiče Vaše baze podataka, ukidanje vidljive lozinke dešava se prvi put " -#~ "pošto se korisnik prijavi u bazu podataka, po instalaciji base_crypt.\n" -#~ "Po instalaciji ovog modula neće biti moguće izmeniti zaboravljenu lozinku za " -#~ "Vaše korisnike, jedino rešenje će biti da im administrator postavi novu " -#~ "lozinku.\n" -#~ "\n" -#~ "Beleška: Instaliranje ovog modula ne znači da možete ignorisati osnovne " -#~ "sigurnosne mere,\n" -#~ "budući da se lozinka i dalje prenosi nešifrovana u mreži (od strane " -#~ "klijenta),\n" -#~ "ukoliko ne koristite siguran protokol kao XML-RPCS.\n" -#~ " " - -#~ msgid "The chosen company is not in the allowed companies for this user" -#~ msgstr "Odabrano preduzeće nije u dozvoljenim preduzećima za ovog korisnioka" diff --git a/addons/auth_crypt/i18n/vi.po b/addons/auth_crypt/i18n/vi.po deleted file mode 100644 index 4ad1a22fdaf..00000000000 --- a/addons/auth_crypt/i18n/vi.po +++ /dev/null @@ -1,80 +0,0 @@ -# Vietnamese translation for openobject-addons -# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011 -# This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2011. -# -msgid "" -msgstr "" -"Project-Id-Version: openobject-addons\n" -"Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2012-12-03 16:03+0000\n" -"PO-Revision-Date: 2011-07-20 09:55+0000\n" -"Last-Translator: OpenBMS JSC \n" -"Language-Team: Vietnamese \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-12-04 05:53+0000\n" -"X-Generator: Launchpad (build 16335)\n" - -#. module: base_crypt -#: model:ir.model,name:base_crypt.model_res_users -msgid "Users" -msgstr "" - -#, python-format -#~ msgid "Error" -#~ msgstr "Lỗi" - -#~ msgid "Base - Password Encryption" -#~ msgstr "Cơ sở - Mã hóa Mật khẩu" - -#, python-format -#~ msgid "Please specify the password !" -#~ msgstr "Vui lòng xác định mật khẩu !" - -#~ msgid "The chosen company is not in the allowed companies for this user" -#~ msgstr "" -#~ "Công ty được lựa chọn không nằm trong các công ty mà người sử dụng này được " -#~ "phép" - -#~ msgid "res.users" -#~ msgstr "res.users" - -#~ msgid "You can not have two users with the same login !" -#~ msgstr "Bạn không thể có hai người sử dụng với cùng tên đăng nhập !" - -#~ msgid "" -#~ "This module replaces the cleartext password in the database with a password " -#~ "hash,\n" -#~ "preventing anyone from reading the original password.\n" -#~ "For your existing user base, the removal of the cleartext passwords occurs " -#~ "the first time\n" -#~ "a user logs into the database, after installing base_crypt.\n" -#~ "After installing this module it won't be possible to recover a forgotten " -#~ "password for your\n" -#~ "users, the only solution is for an admin to set a new password.\n" -#~ "\n" -#~ "Note: installing this module does not mean you can ignore basic security " -#~ "measures,\n" -#~ "as the password is still transmitted unencrypted on the network (by the " -#~ "client),\n" -#~ "unless you are using a secure protocol such as XML-RPCS.\n" -#~ " " -#~ msgstr "" -#~ "Mô-đun này thay thế mật khẩu dạng tường minh (cleartext) trong cơ sở dữ liệu " -#~ "với một mật khẩu băm (hash),\n" -#~ "ngăn chặn bất cứ ai đọc các mật khẩu ban đầu.\n" -#~ "Đối với người dùng hiện tại của bạn, việc loại bỏ các mật khẩu tường minh " -#~ "xảy ra lần đầu tiên\n" -#~ "người dùng đăng nhập vào cơ sở dữ liệu, sau khi cài đặt base_crypt.\n" -#~ "Sau khi cài đặt mô-đun này, sẽ không thể khôi phục lại một mật khẩu bị lãng " -#~ "quên cho\n" -#~ "người sử dụng của bạn, giải pháp duy nhất là để một quản trị viên thiết lập " -#~ "một mật khẩu mới.\n" -#~ "\n" -#~ "Lưu ý: cài đặt mô-đun này không có nghĩa là bạn có thể bỏ qua các biện pháp " -#~ "bảo mật cơ bản,\n" -#~ "như mật khẩu vẫn được truyền không mã hóa trên mạng (từ máy khách),\n" -#~ "trừ khi bạn sử dụng một giao thức an toàn chẳng hạn như XML-RPCS.\n" -#~ " " diff --git a/addons/auth_ldap/users_ldap.py b/addons/auth_ldap/users_ldap.py index e6a7f0f4716..9ddc3d6ed45 100644 --- a/addons/auth_ldap/users_ldap.py +++ b/addons/auth_ldap/users_ldap.py @@ -237,14 +237,14 @@ class res_company(osv.osv): _inherit = "res.company" _columns = { 'ldaps': fields.one2many( - 'res.company.ldap', 'company', 'LDAP Parameters'), + 'res.company.ldap', 'company', 'LDAP Parameters', copy=True), } class users(osv.osv): _inherit = "res.users" - def login(self, db, login, password): - user_id = super(users, self).login(db, login, password) + def _login(self, db, login, password): + user_id = super(users, self)._login(db, login, password) if user_id: return user_id registry = RegistryManager.get(db) diff --git a/addons/auth_oauth/res_users.py b/addons/auth_oauth/res_users.py index 8d57a23f702..d2aed236f75 100644 --- a/addons/auth_oauth/res_users.py +++ b/addons/auth_oauth/res_users.py @@ -17,8 +17,8 @@ class res_users(osv.Model): _columns = { 'oauth_provider_id': fields.many2one('auth.oauth.provider', 'OAuth Provider'), - 'oauth_uid': fields.char('OAuth User ID', help="Oauth Provider user_id"), - 'oauth_access_token': fields.char('OAuth Access Token', readonly=True), + 'oauth_uid': fields.char('OAuth User ID', help="Oauth Provider user_id", copy=False), + 'oauth_access_token': fields.char('OAuth Access Token', readonly=True, copy=False), } _sql_constraints = [ diff --git a/addons/auth_openid/res_users.py b/addons/auth_openid/res_users.py index 897dea07982..1c6c635118a 100644 --- a/addons/auth_openid/res_users.py +++ b/addons/auth_openid/res_users.py @@ -30,11 +30,11 @@ class res_users(osv.osv): # TODO create helper fields for autofill openid_url and openid_email -> http://pad.openerp.com/web-openid _columns = { - 'openid_url': fields.char('OpenID URL', size=1024), - 'openid_email': fields.char('OpenID Email', size=256, + 'openid_url': fields.char('OpenID URL', size=1024, copy=False), + 'openid_email': fields.char('OpenID Email', size=256, copy=False, help="Used for disambiguation in case of a shared OpenID URL"), 'openid_key': fields.char('OpenID Key', size=utils.KEY_LENGTH, - readonly=True), + readonly=True, copy=False), } def _check_openid_url_email(self, cr, uid, ids, context=None): @@ -48,19 +48,8 @@ class res_users(osv.osv): (_check_openid_url_email, lambda self, *a, **kw: self._check_openid_url_email_msg(*a, **kw), ['active', 'openid_url', 'openid_email']), ] - def copy(self, cr, uid, rid, defaults=None, context=None): - reset_fields = 'openid_url openid_email'.split() - reset_values = dict.fromkeys(reset_fields, False) - if defaults is None: - defaults = reset_values - else: - defaults = dict(reset_values, **defaults) - - defaults['openid_key'] = False - return super(res_users, self).copy(cr, uid, rid, defaults, context) - - def login(self, db, login, password): - result = super(res_users, self).login(db, login, password) + def _login(self, db, login, password): + result = super(res_users, self)._login(db, login, password) if result: return result else: @@ -69,6 +58,7 @@ class res_users(osv.osv): SET login_date=now() AT TIME ZONE 'UTC' WHERE login=%s AND openid_key=%s AND active=%s RETURNING id""", (tools.ustr(login), tools.ustr(password), True)) + # beware: record cache may be invalid res = cr.fetchone() cr.commit() return res[0] if res else False diff --git a/addons/auth_signup/res_users.py b/addons/auth_signup/res_users.py index 576a778be45..97bcea52bc1 100644 --- a/addons/auth_signup/res_users.py +++ b/addons/auth_signup/res_users.py @@ -104,9 +104,9 @@ class res_partner(osv.Model): return self._get_signup_url_for_action(cr, uid, ids, context=context) _columns = { - 'signup_token': fields.char('Signup Token'), - 'signup_type': fields.char('Signup Token Type'), - 'signup_expiration': fields.datetime('Signup Expiration'), + 'signup_token': fields.char('Signup Token', copy=False), + 'signup_type': fields.char('Signup Token Type', copy=False), + 'signup_expiration': fields.datetime('Signup Expiration', copy=False), 'signup_valid': fields.function(_get_signup_valid, type='boolean', string='Signup Token is Valid'), 'signup_url': fields.function(_get_signup_url, type='char', string='Signup URL'), } @@ -302,7 +302,7 @@ class res_users(osv.Model): user_id = super(res_users, self).create(cr, uid, values, context=context) user = self.browse(cr, uid, user_id, context=context) if user.email and not context.get('no_reset_password'): - context.update({'create_user': True}) + context = dict(context, create_user=True) try: self.action_reset_password(cr, uid, [user.id], context=context) except MailDeliveryException: diff --git a/addons/auth_signup/signup.xml b/addons/auth_signup/signup.xml deleted file mode 100644 index d01ee58f2f1..00000000000 --- a/addons/auth_signup/signup.xml +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - signup.signup.form - signup.signup - form - -
- - - - - - - -
Passwords missmatch
-
- -