From dcb6249d96b3f4a507d88601ca74dc8af75484b2 Mon Sep 17 00:00:00 2001 From: brian10048 Date: Tue, 26 Nov 2019 16:59:14 -0500 Subject: [PATCH 01/33] [IMP] fieldservice_account: Simplify module - This change removes most of the invoicing logic from fieldservice_account and puts it into its own module --- fieldservice_isp_account/README.rst | 121 +++++ fieldservice_isp_account/__init__.py | 4 + fieldservice_isp_account/__manifest__.py | 26 + .../data/time_products.xml | 36 ++ .../models/account_invoice_line.py | 34 ++ fieldservice_isp_account/models/fsm_order.py | 184 +++++++ fieldservice_isp_account/models/fsm_person.py | 29 ++ fieldservice_isp_account/readme/CONFIGURE.rst | 1 + .../readme/CONTRIBUTORS.rst | 3 + fieldservice_isp_account/readme/CREDITS.rst | 3 + .../readme/DESCRIPTION.rst | 4 + fieldservice_isp_account/readme/INSTALL.rst | 1 + fieldservice_isp_account/readme/USAGE.rst | 12 + .../static/description/icon.png | Bin 0 -> 17808 bytes .../static/description/index.html | 460 ++++++++++++++++++ fieldservice_isp_account/tests/__init__.py | 4 + .../tests/test_isp_account.py | 178 +++++++ fieldservice_isp_account/views/account.xml | 29 ++ fieldservice_isp_account/views/fsm_order.xml | 89 ++++ fieldservice_isp_account/views/fsm_person.xml | 21 + .../views/hr_timesheet.xml | 36 ++ 21 files changed, 1275 insertions(+) create mode 100644 fieldservice_isp_account/README.rst create mode 100644 fieldservice_isp_account/__init__.py create mode 100644 fieldservice_isp_account/__manifest__.py create mode 100644 fieldservice_isp_account/data/time_products.xml create mode 100644 fieldservice_isp_account/models/account_invoice_line.py create mode 100644 fieldservice_isp_account/models/fsm_order.py create mode 100644 fieldservice_isp_account/models/fsm_person.py create mode 100644 fieldservice_isp_account/readme/CONFIGURE.rst create mode 100644 fieldservice_isp_account/readme/CONTRIBUTORS.rst create mode 100644 fieldservice_isp_account/readme/CREDITS.rst create mode 100644 fieldservice_isp_account/readme/DESCRIPTION.rst create mode 100644 fieldservice_isp_account/readme/INSTALL.rst create mode 100644 fieldservice_isp_account/readme/USAGE.rst create mode 100644 fieldservice_isp_account/static/description/icon.png create mode 100644 fieldservice_isp_account/static/description/index.html create mode 100644 fieldservice_isp_account/tests/__init__.py create mode 100644 fieldservice_isp_account/tests/test_isp_account.py create mode 100644 fieldservice_isp_account/views/account.xml create mode 100644 fieldservice_isp_account/views/fsm_order.xml create mode 100644 fieldservice_isp_account/views/fsm_person.xml create mode 100644 fieldservice_isp_account/views/hr_timesheet.xml diff --git a/fieldservice_isp_account/README.rst b/fieldservice_isp_account/README.rst new file mode 100644 index 0000000000..278e295c55 --- /dev/null +++ b/fieldservice_isp_account/README.rst @@ -0,0 +1,121 @@ +========================== +Field Service - Accounting +========================== + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Ffield--service-lightgray.png?logo=github + :target: https://github.com/OCA/field-service/tree/12.0/fieldservice_account + :alt: OCA/field-service +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/field-service-12-0/field-service-12-0-fieldservice_account + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png + :target: https://runbot.odoo-community.org/runbot/264/12.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module adds the ability to track employee time and contractor +costs for Field Service Orders. It also adds functionality to create +a customer invoice and a vendor bill when a Field Service Order is +completed. + +**Table of contents** + +.. contents:: + :local: + +Installation +============ + +No special installation instructions + +Configuration +============= + +To configure this module, you need to: + +* Go to Field Service > Master Data > Locations +* Create or select a location and set their analytic account + +Usage +===== + +To use the module: + +On a field service order, open the "Accounting" tab. Depending on +whether the logged in user or an employee or not, they will see +either a place to enter timesheet records or contractor costs. + +The total cost of the order is calculated based on the entries in +the employee timesheet entries and contractor costs. + +When an order is completed, a customer invoice will be generated for +the employee time and the contractor costs. A vendor bill will be +created for the contractor costs. + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* Open Source Integrators + +Contributors +~~~~~~~~~~~~ + +* Michael Allen +* Serpent Consulting Services Pvt. Ltd. + +Other credits +~~~~~~~~~~~~~ + +The development of this module has been financially supported by: + +* Open Source Integrators + +Maintainers +~~~~~~~~~~~ + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +.. |maintainer-osimallen| image:: https://github.com/osimallen.png?size=40px + :target: https://github.com/osimallen + :alt: osimallen + +Current `maintainer `__: + +|maintainer-osimallen| + +This module is part of the `OCA/field-service `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/fieldservice_isp_account/__init__.py b/fieldservice_isp_account/__init__.py new file mode 100644 index 0000000000..66ca882fcc --- /dev/null +++ b/fieldservice_isp_account/__init__.py @@ -0,0 +1,4 @@ +# Copyright (C) 2018 Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import models diff --git a/fieldservice_isp_account/__manifest__.py b/fieldservice_isp_account/__manifest__.py new file mode 100644 index 0000000000..cd184a7c82 --- /dev/null +++ b/fieldservice_isp_account/__manifest__.py @@ -0,0 +1,26 @@ +# Copyright (C) 2018 Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + 'name': 'Field Service - ISP Accounting', + 'summary': """Invoice Field Service orders based on employee time + or contractor costs""", + 'version': '12.0.2.0.1', + 'category': 'Field Service', + 'author': 'Open Source Integrators, Odoo Community Association (OCA)', + 'website': 'https://github.com/OCA/field-service', + 'depends': [ + 'fieldservice_account_analytic', + 'hr_timesheet', + ], + 'data': [ + 'data/time_products.xml', + 'views/account.xml', + 'views/fsm_order.xml', + 'views/fsm_person.xml', + 'views/hr_timesheet.xml', + ], + 'license': 'AGPL-3', + 'development_status': 'Beta', + 'maintainers': ['osimallen'], +} diff --git a/fieldservice_isp_account/data/time_products.xml b/fieldservice_isp_account/data/time_products.xml new file mode 100644 index 0000000000..96a1a4bee3 --- /dev/null +++ b/fieldservice_isp_account/data/time_products.xml @@ -0,0 +1,36 @@ + + + + + FSM Travel Time + + service + + + Travel time for Field Service + Employees + + + + + FSM Regular Time + + service + + + Regular time for Field Service + Employees + + + + + FSM Overtime + + service + + + Overtime for Field Service Employees + + + + diff --git a/fieldservice_isp_account/models/account_invoice_line.py b/fieldservice_isp_account/models/account_invoice_line.py new file mode 100644 index 0000000000..1f64c10bcb --- /dev/null +++ b/fieldservice_isp_account/models/account_invoice_line.py @@ -0,0 +1,34 @@ +# Copyright (C) 2018 - TODAY, Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, models, _ +from odoo.exceptions import ValidationError + + +class AccountInvoiceLine(models.Model): + _inherit = "account.invoice.line" + + @api.onchange('product_id', 'quantity') + def onchange_product_id(self): + for line in self: + if line.fsm_order_id: + partner = line.fsm_order_id.person_id and\ + line.fsm_order_id.person_id.partner_id or False + if not partner: + raise ValidationError( + _("Please set the field service worker.")) + fpos = partner.property_account_position_id + tmpl = line.product_id.product_tmpl_id + if line.product_id: + accounts = tmpl.get_product_accounts() + supinfo = self.env['product.supplierinfo'].search( + [('name', '=', partner.id), + ('product_tmpl_id', '=', tmpl.id), + ('min_qty', '<=', line.quantity)], + order='min_qty DESC') + line.price_unit = \ + supinfo and supinfo[0].price or tmpl.standard_price + line.account_id = accounts.get('expense', False) + line.invoice_line_tax_ids = fpos.\ + map_tax(tmpl.supplier_taxes_id) + line.name = line.product_id.name diff --git a/fieldservice_isp_account/models/fsm_order.py b/fieldservice_isp_account/models/fsm_order.py new file mode 100644 index 0000000000..9cd3454bb3 --- /dev/null +++ b/fieldservice_isp_account/models/fsm_order.py @@ -0,0 +1,184 @@ +# Copyright (C) 2018 - TODAY, Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models, _ +from odoo.exceptions import ValidationError + + +ACCOUNT_STAGES = [('draft', 'Draft'), + ('review', 'Needs Review'), + ('confirmed', 'Confirmed'), + ('invoiced', 'Fully Invoiced'), + ('no', 'Nothing Invoiced')] + + +class FSMOrder(models.Model): + _inherit = 'fsm.order' + + contractor_cost_ids = fields.One2many('account.invoice.line', + 'fsm_order_id', + string='Contractor Costs') + employee_timesheet_ids = fields.One2many('account.analytic.line', + "fsm_order_id", + string='Employee Timesheets') + employee = fields.Boolean(compute='_compute_employee') + contractor_total = fields.Float(compute='_compute_contractor_cost', + string='Contractor Cost Estimate') + employee_time_total = fields.Float(compute='_compute_employee_hours', + string='Total Employee Hours') + account_stage = fields.Selection(ACCOUNT_STAGES, string='Accounting Stage', + default='draft') + + def _compute_employee(self): + user = self.env['res.users'].browse(self.env.uid) + for order in self: + if user.employee_ids: + order.employee = True + + @api.depends('employee_timesheet_ids', 'contractor_cost_ids') + def _compute_total_cost(self): + super(self)._compute_total_cost() + for order in self: + order.total_cost = 0.0 + rate = 0 + for line in order.employee_timesheet_ids: + rate = line.employee_id.timesheet_cost + order.total_cost += line.unit_amount * rate + for cost in order.contractor_cost_ids: + order.total_cost += cost.price_unit * cost.quantity + + @api.depends('employee_timesheet_ids') + def _compute_employee_hours(self): + for order in self: + order.employee_time_total = 0.0 + for line in order.employee_timesheet_ids: + order.employee_time_total += line.unit_amount + + @api.depends('contractor_cost_ids') + def _compute_contractor_cost(self): + for order in self: + order.contractor_total = 0.0 + for cost in order.contractor_cost_ids: + order.contractor_total += cost.price_unit * cost.quantity + + def action_complete(self): + for order in self: + order.account_stage = 'review' + if self.person_id.supplier and not self.contractor_cost_ids: + raise ValidationError(_("Cannot move to Complete " + + "until 'Contractor Costs' is filled in")) + if not self.person_id.supplier and not self.employee_timesheet_ids: + raise ValidationError(_("Cannot move to Complete until " + + "'Employee Timesheets' is filled in")) + return super(FSMOrder, self).action_complete() + + def create_bills(self): + jrnl = self.env['account.journal'].search([ + ('company_id', '=', self.env.user.company_id.id), + ('type', '=', 'purchase'), + ('active', '=', True)], + limit=1) + fpos = self.customer_id.property_account_position_id + vals = { + 'partner_id': self.person_id.partner_id.id, + 'type': 'in_invoice', + 'journal_id': jrnl.id or False, + 'fiscal_position_id': fpos.id or False, + 'fsm_order_id': self.id + } + bill = self.env['account.invoice'].sudo().create(vals) + for line in self.contractor_cost_ids: + line.invoice_id = bill + bill.compute_taxes() + + def account_confirm(self): + for order in self: + contractor = order.person_id.partner_id.supplier + if order.contractor_cost_ids: + if contractor: + order.create_bills() + order.account_stage = 'confirmed' + else: + raise ValidationError(_("The worker assigned to this order" + " is not a supplier")) + if order.employee_timesheet_ids: + order.account_stage = 'confirmed' + + def account_create_invoice(self): + jrnl = self.env['account.journal'].search([ + ('company_id', '=', self.env.user.company_id.id), + ('type', '=', 'sale'), + ('active', '=', True)], + limit=1) + if self.bill_to == 'contact': + if not self.customer_id: + raise ValidationError(_("Contact empty")) + fpos = self.customer_id.property_account_position_id + vals = { + 'partner_id': self.customer_id.id, + 'type': 'out_invoice', + 'journal_id': jrnl.id or False, + 'fiscal_position_id': fpos.id or False, + 'fsm_order_id': self.id + } + invoice = self.env['account.invoice'].sudo().create(vals) + price_list = invoice.partner_id.property_product_pricelist + else: + fpos = self.location_id.customer_id.property_account_position_id + vals = { + 'partner_id': self.location_id.customer_id.id, + 'type': 'out_invoice', + 'journal_id': jrnl.id or False, + 'fiscal_position_id': fpos.id or False, + 'fsm_order_id': self.id + } + invoice = self.env['account.invoice'].sudo().create(vals) + price_list = invoice.partner_id.property_product_pricelist + for line in self.employee_timesheet_ids: + price = price_list.get_product_price(product=line.product_id, + quantity=line.unit_amount, + partner=invoice.partner_id, + date=False, + uom_id=False) + template = line.product_id.product_tmpl_id + accounts = template.get_product_accounts() + account = accounts['income'] + vals = { + 'product_id': line.product_id.id, + 'account_analytic_id': line.account_id.id, + 'quantity': line.unit_amount, + 'name': line.name, + 'price_unit': price, + 'account_id': account.id, + 'invoice_id': invoice.id + } + time_cost = self.env['account.invoice.line'].create(vals) + taxes = template.taxes_id + time_cost.invoice_line_tax_ids = fpos.map_tax(taxes) + for cost in self.contractor_cost_ids: + price = price_list.get_product_price(product=cost.product_id, + quantity=cost.quantity, + partner=invoice.partner_id, + date=False, + uom_id=False) + template = cost.product_id.product_tmpl_id + accounts = template.get_product_accounts() + account = accounts['income'] + vals = { + 'product_id': cost.product_id.id, + 'account_analytic_id': cost.account_analytic_id.id, + 'quantity': cost.quantity, + 'name': cost.name, + 'price_unit': price, + 'account_id': account.id, + 'invoice_id': invoice.id + } + con_cost = self.env['account.invoice.line'].create(vals) + taxes = template.taxes_id + con_cost.invoice_line_tax_ids = fpos.map_tax(taxes) + invoice.compute_taxes() + self.account_stage = 'invoiced' + return invoice + + def account_no_invoice(self): + self.account_stage = 'no' diff --git a/fieldservice_isp_account/models/fsm_person.py b/fieldservice_isp_account/models/fsm_person.py new file mode 100644 index 0000000000..1b75acfaa6 --- /dev/null +++ b/fieldservice_isp_account/models/fsm_person.py @@ -0,0 +1,29 @@ +# Copyright (C) 2018 - TODAY, Open Source Integrators +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models + + +class FSMPerson(models.Model): + _inherit = 'fsm.person' + + bill_count = fields.Integer(string='Vendor Bills', + compute='_compute_vendor_bills') + + def _compute_vendor_bills(self): + self.bill_count = self.env['account.invoice'].search_count([ + ('partner_id', '=', self.partner_id.id)]) + + @api.multi + def action_view_bills(self): + for bill in self: + action = self.env.ref('account.action_invoice_tree1').read()[0] + vendor_bills = self.env['account.invoice'].search( + [('partner_id', '=', bill.partner_id.id)]) + if len(vendor_bills) == 1: + action['views'] = [ + (self.env.ref('account.invoice_form').id, 'form')] + action['res_id'] = vendor_bills.id + else: + action['domain'] = [('id', 'in', vendor_bills.ids)] + return action diff --git a/fieldservice_isp_account/readme/CONFIGURE.rst b/fieldservice_isp_account/readme/CONFIGURE.rst new file mode 100644 index 0000000000..89a40f9121 --- /dev/null +++ b/fieldservice_isp_account/readme/CONFIGURE.rst @@ -0,0 +1 @@ +No special configuration instructions. diff --git a/fieldservice_isp_account/readme/CONTRIBUTORS.rst b/fieldservice_isp_account/readme/CONTRIBUTORS.rst new file mode 100644 index 0000000000..e9143c46f2 --- /dev/null +++ b/fieldservice_isp_account/readme/CONTRIBUTORS.rst @@ -0,0 +1,3 @@ +* Michael Allen +* Serpent Consulting Services Pvt. Ltd. +* Brian McMaster diff --git a/fieldservice_isp_account/readme/CREDITS.rst b/fieldservice_isp_account/readme/CREDITS.rst new file mode 100644 index 0000000000..0eff0acf4e --- /dev/null +++ b/fieldservice_isp_account/readme/CREDITS.rst @@ -0,0 +1,3 @@ +The development of this module has been financially supported by: + +* Open Source Integrators diff --git a/fieldservice_isp_account/readme/DESCRIPTION.rst b/fieldservice_isp_account/readme/DESCRIPTION.rst new file mode 100644 index 0000000000..127d578738 --- /dev/null +++ b/fieldservice_isp_account/readme/DESCRIPTION.rst @@ -0,0 +1,4 @@ +This module adds the ability to track employee time and contractor +costs for Field Service Orders. It also adds functionality to create +a customer invoice and a vendor bill when a Field Service Order is +completed. diff --git a/fieldservice_isp_account/readme/INSTALL.rst b/fieldservice_isp_account/readme/INSTALL.rst new file mode 100644 index 0000000000..54fea7becb --- /dev/null +++ b/fieldservice_isp_account/readme/INSTALL.rst @@ -0,0 +1 @@ +No special installation instructions diff --git a/fieldservice_isp_account/readme/USAGE.rst b/fieldservice_isp_account/readme/USAGE.rst new file mode 100644 index 0000000000..c730bb37dd --- /dev/null +++ b/fieldservice_isp_account/readme/USAGE.rst @@ -0,0 +1,12 @@ +To use the module: + +On a field service order, open the "Accounting" tab. Depending on +whether the logged in user or an employee or not, they will see +either a place to enter timesheet records or contractor costs. + +The total cost of the order is calculated based on the entries in +the employee timesheet entries and contractor costs. + +When an order is completed, a customer invoice will be generated for +the employee time and the contractor costs. A vendor bill will be +created for the contractor costs. diff --git a/fieldservice_isp_account/static/description/icon.png b/fieldservice_isp_account/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..955674d8f0b8c47de3ffa9db25cb109fbe4a1091 GIT binary patch literal 17808 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 literal 0 HcmV?d00001 diff --git a/fieldservice_isp_account/static/description/index.html b/fieldservice_isp_account/static/description/index.html new file mode 100644 index 0000000000..be45db7518 --- /dev/null +++ b/fieldservice_isp_account/static/description/index.html @@ -0,0 +1,460 @@ + + + + + + +Field Service - Accounting + + + +
+

Field Service - Accounting

+ + +

Beta License: AGPL-3 OCA/field-service Translate me on Weblate Try me on Runbot

+

This module adds the ability to track employee time and contractor +costs for Field Service Orders. It also adds functionality to create +a customer invoice and a vendor bill when a Field Service Order is +completed.

+

Table of contents

+ +
+

Installation

+

No special installation instructions

+
+
+

Configuration

+

To configure this module, you need to:

+
    +
  • Go to Field Service > Master Data > Locations
  • +
  • Create or select a location and set their analytic account
  • +
+
+
+

Usage

+

To use the module:

+

On a field service order, open the “Accounting” tab. Depending on +whether the logged in user or an employee or not, they will see +either a place to enter timesheet records or contractor costs.

+

The total cost of the order is calculated based on the entries in +the employee timesheet entries and contractor costs.

+

When an order is completed, a customer invoice will be generated for +the employee time and the contractor costs. A vendor bill will be +created for the contractor costs.

+
+
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • Open Source Integrators
  • +
+
+
+

Contributors

+ +
+
+

Other credits

+

The development of this module has been financially supported by:

+ +
+
+

Maintainers

+

This module is maintained by the OCA.

+Odoo Community Association +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

Current maintainer:

+

osimallen

+

This module is part of the OCA/field-service project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + diff --git a/fieldservice_isp_account/tests/__init__.py b/fieldservice_isp_account/tests/__init__.py new file mode 100644 index 0000000000..6446a7a2b2 --- /dev/null +++ b/fieldservice_isp_account/tests/__init__.py @@ -0,0 +1,4 @@ +# Copyright 2019 Ecosoft Co., Ltd (http://ecosoft.co.th/) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) + +from . import test_isp_account diff --git a/fieldservice_isp_account/tests/test_isp_account.py b/fieldservice_isp_account/tests/test_isp_account.py new file mode 100644 index 0000000000..60dd8b1de2 --- /dev/null +++ b/fieldservice_isp_account/tests/test_isp_account.py @@ -0,0 +1,178 @@ +# Copyright 2019 Ecosoft Co., Ltd (http://ecosoft.co.th/) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) + +from odoo import fields +from odoo.addons.fieldservice_sale.tests.test_fsm_account import ( + FSMAccountCase +) +from odoo.exceptions import ValidationError + + +class FSMAccountCase(FSMAccountCase): + + def setUp(self): + super(FSMAccountCase, self).setUp() + self.test_person = self.env['fsm.person'].create({'name': 'Worker-1'}) + self.test_analytic = self.env.ref('analytic.analytic_administratif') + self.account_id = self.env['account.account'].create({ + 'code': 'NC1110', + 'name': 'Test Payable Account', + 'user_type_id': + self.env.ref('account.data_account_type_payable').id, + 'reconcile': True + }) + + def _create_workorder(self, bill_to, contractors, timesheets): + # Create a new work order + contractors = self.env['account.invoice.line'].create(contractors) + timesheets = self.env['account.analytic.line'].create(timesheets) + + order = self.env['fsm.order'].\ + create({ + 'location_id': self.test_location.id, + 'bill_to': bill_to, + 'person_id': self.test_person.id, + 'contractor_cost_ids': [(6, 0, contractors.ids)], + 'employee_timesheet_ids': [(6, 0, timesheets.ids)] + }) + order.person_ids += self.test_person + return order + + def _process_order_to_invoices(self, order): + # Change states + order.date_start = fields.Datetime.today() + order.date_end = fields.Datetime.today() + order.resolution = 'Done something!' + order.action_complete() + self.assertEqual(order.account_stage, 'review') + # Create vendor bill + # Vendor bill created from order's contractor + if not order.person_id.partner_id.supplier: + with self.assertRaises(ValidationError) as e: + order.account_confirm() + self.assertEqual( + e.exception.name, + "The worker assigned to this order is not a supplier") + order.person_id.partner_id.supplier = True + order.account_confirm() + self.assertEqual(order.account_stage, 'confirmed') + bill = self.AccountInvoice.search([('type', '=', 'in_invoice'), + ('fsm_order_id', '=', order.id)]) + self.assertEqual(len(bill), 1) + self.assertEqual(len(order.contractor_cost_ids), + len(bill.invoice_line_ids)) + # Customer invoice created from order's contractor and timehsheet + if order.bill_to == 'contact' and not order.customer_id: + with self.assertRaises(ValidationError): + order.account_create_invoice() + order.customer_id = self.test_loc_partner # Assign some partner + order.account_create_invoice() + self.assertEqual(order.account_stage, 'invoiced') + invoice = self.AccountInvoice.search([('type', '=', 'out_invoice'), + ('fsm_order_id', '=', order.id)]) + self.assertEqual(len(invoice), 1) + self.assertEqual( + len(order.contractor_cost_ids) + len(order.employee_timesheet_ids), + len(invoice.invoice_line_ids)) + return (bill, invoice) + + def test_fsm_order_exception(self): + """Create a new work order, error raised when + - If person_is is not set, but user try to add new contractor_cost_ids + - If analytic account is not set in location, + and user create contractor_cost_ids (account.move.line) + """ + # Test if the person_id is not selected, error when add contractor line + # Setup required data + self.test_location.analytic_account_id = self.test_analytic + # Create a new work order with contract = 500 and timesheet = 300 + self.env.ref('hr.employee_qdp').timesheet_cost = 20.0 + order = self.env['fsm.order'].\ + create({ + 'location_id': self.test_location.id, + 'person_id': self.test_person.id, + }) + order.person_id = self.test_person + + order.person_ids += self.test_person + order.date_start = fields.Datetime.today() + order.date_end = fields.Datetime.today() + order.resolution = 'Done something!' + with self.assertRaises(ValidationError) as e: + order.action_complete() + self.assertEqual(e.exception.name, + "Cannot move to Complete until " + "'Employee Timesheets' is filled in") + + def test_fsm_order_bill_to_location(self): + """Bill To Location, + invoice created is based on this order's location's partner + """ + # Setup required data + self.test_location.analytic_account_id = self.test_analytic + contractors = [{'name': 'contractor_line_1', + 'product_id': self.env.ref('product.expense_hotel').id, + 'account_id': self.account_id.id, + 'quantity': 2, + 'price_unit': 200}, ] + self.env.ref('hr.employee_qdp').timesheet_cost = 100 + timesheets = [{'name': 'timesheet_line_1', + 'employee_id': self.env.ref('hr.employee_qdp').id, + 'account_id': self.test_analytic.id, + 'product_id': self.env.ref('product.expense_hotel').id, + 'unit_amount': 6}, + {'name': 'timesheet_line_2', + 'employee_id': self.env.ref('hr.employee_qdp').id, + 'account_id': self.test_analytic.id, + 'product_id': self.env.ref('product.expense_hotel').id, + 'unit_amount': 4}, ] + order = self._create_workorder(bill_to='location', + contractors=contractors, + timesheets=timesheets) + order._compute_contractor_cost() + order._compute_employee_hours() + order._compute_total_cost() + self.assertEqual(order.contractor_total, 400) + self.assertEqual(order.employee_time_total, 10) # Hrs + self.assertEqual(order.total_cost, 1400) + # Testing not working "Need to Configure Chart of Accounts" + # bill, invoice = self._process_order_to_invoices(order) + # self.assertEqual(bill.partner_id, order.person_id.partner_id) + # self.assertEqual(invoice.partner_id, order.location_id.customer_id) + + def test_fsm_order_bill_to_contact(self): + """Bill To Contact, + invoice created is based on this order's contact + """ + # Setup required data + self.test_location.analytic_account_id = self.test_analytic + # Create a new work order with contract = 500 and timesheet = 300 + contractors = [{'name': 'contractor_line_2', + 'product_id': self.env.ref('product.expense_hotel').id, + 'account_id': self.account_id.id, + 'quantity': 2, + 'price_unit': 100}, + {'name': 'contractor_line_3', + 'product_id': self.env.ref('product.expense_hotel').id, + 'account_id': self.account_id.id, + 'quantity': 1, + 'price_unit': 300}, ] + self.env.ref('hr.employee_qdp').timesheet_cost = 20.0 + timesheets = [{'name': 'timesheet_line_3', + 'employee_id': self.env.ref('hr.employee_qdp').id, + 'account_id': self.test_analytic.id, + 'product_id': self.env.ref('product.expense_hotel').id, + 'unit_amount': 10}, ] + order = self._create_workorder(bill_to='contact', + contractors=contractors, + timesheets=timesheets) + order._compute_contractor_cost() + order._compute_employee_hours() + order._compute_total_cost() + self.assertEqual(order.contractor_total, 500) + self.assertEqual(order.employee_time_total, 10) # Hrs + self.assertEqual(order.total_cost, 700) + # Testing not working "Need to Configure Chart of Accounts" + # bill, invoice = self._process_order_to_invoices(order) + # self.assertEqual(bill.partner_id, order.person_id.partner_id) + # self.assertEqual(invoice.partner_id, order.customer_id) diff --git a/fieldservice_isp_account/views/account.xml b/fieldservice_isp_account/views/account.xml new file mode 100644 index 0000000000..3e7aeb49e5 --- /dev/null +++ b/fieldservice_isp_account/views/account.xml @@ -0,0 +1,29 @@ + + + + + Service Orders + fsm.order + form + tree,form + + +

+ No Field Service Orders to review. +

+
+
+ + + + + +
diff --git a/fieldservice_isp_account/views/fsm_order.xml b/fieldservice_isp_account/views/fsm_order.xml new file mode 100644 index 0000000000..4540901734 --- /dev/null +++ b/fieldservice_isp_account/views/fsm_order.xml @@ -0,0 +1,89 @@ + + + + fsm.order.form.isp.account + fsm.order + + + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + fsm.order.search + fsm.order + + + + + + + + +
diff --git a/fieldservice_isp_account/views/fsm_person.xml b/fieldservice_isp_account/views/fsm_person.xml new file mode 100644 index 0000000000..0f2a42a1a5 --- /dev/null +++ b/fieldservice_isp_account/views/fsm_person.xml @@ -0,0 +1,21 @@ + + + + fsm.person.form.isp.account + fsm.person + + +
+ +
+
+
+ +
diff --git a/fieldservice_isp_account/views/hr_timesheet.xml b/fieldservice_isp_account/views/hr_timesheet.xml new file mode 100644 index 0000000000..25ebf8b849 --- /dev/null +++ b/fieldservice_isp_account/views/hr_timesheet.xml @@ -0,0 +1,36 @@ + + + + My Timesheets + account.analytic.line + tree,form + [('user_id', '=', uid)] + { + "search_default_week":1, + } + + +

+ Record a new activity +

+ You can register and track your workings hours by project every + day. Every time spent on a project will become a cost and can be re-invoiced to + customers if required. +

+
+
+ + + + account.analytic.line.timesheet.user + + + [('user_id', '=', user.id), ('project_id', '!=', False)] + + + + + + + +
From a58b70cd6313632a3b6a0ffe4f439eaf23a1a466 Mon Sep 17 00:00:00 2001 From: brian10048 Date: Mon, 2 Dec 2019 17:32:47 -0500 Subject: [PATCH 02/33] [FIX] fieldservice_account: tests and travis --- fieldservice_isp_account/models/__init__.py | 7 +++++++ fieldservice_isp_account/models/fsm_order.py | 2 +- fieldservice_isp_account/tests/test_isp_account.py | 2 +- fieldservice_isp_account/views/fsm_order.xml | 3 ++- 4 files changed, 11 insertions(+), 3 deletions(-) create mode 100644 fieldservice_isp_account/models/__init__.py diff --git a/fieldservice_isp_account/models/__init__.py b/fieldservice_isp_account/models/__init__.py new file mode 100644 index 0000000000..19e859522b --- /dev/null +++ b/fieldservice_isp_account/models/__init__.py @@ -0,0 +1,7 @@ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import ( + account_invoice_line, + fsm_order, + fsm_person, +) diff --git a/fieldservice_isp_account/models/fsm_order.py b/fieldservice_isp_account/models/fsm_order.py index 9cd3454bb3..4b0637fd24 100644 --- a/fieldservice_isp_account/models/fsm_order.py +++ b/fieldservice_isp_account/models/fsm_order.py @@ -37,7 +37,7 @@ def _compute_employee(self): @api.depends('employee_timesheet_ids', 'contractor_cost_ids') def _compute_total_cost(self): - super(self)._compute_total_cost() + super()._compute_total_cost() for order in self: order.total_cost = 0.0 rate = 0 diff --git a/fieldservice_isp_account/tests/test_isp_account.py b/fieldservice_isp_account/tests/test_isp_account.py index 60dd8b1de2..a12cc2de8d 100644 --- a/fieldservice_isp_account/tests/test_isp_account.py +++ b/fieldservice_isp_account/tests/test_isp_account.py @@ -2,7 +2,7 @@ # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) from odoo import fields -from odoo.addons.fieldservice_sale.tests.test_fsm_account import ( +from odoo.addons.fieldservice_account.tests.test_fsm_account import ( FSMAccountCase ) from odoo.exceptions import ValidationError diff --git a/fieldservice_isp_account/views/fsm_order.xml b/fieldservice_isp_account/views/fsm_order.xml index 4540901734..469e19c4c4 100644 --- a/fieldservice_isp_account/views/fsm_order.xml +++ b/fieldservice_isp_account/views/fsm_order.xml @@ -3,7 +3,8 @@ fsm.order.form.isp.account fsm.order - +
From 1c7048e5b4a4469e1de3a454bc88d2ec177991ea Mon Sep 17 00:00:00 2001 From: brian10048 Date: Thu, 5 Dec 2019 13:28:31 -0500 Subject: [PATCH 03/33] [IMP] fieldservice_account: Update maintainers --- fieldservice_isp_account/__manifest__.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/fieldservice_isp_account/__manifest__.py b/fieldservice_isp_account/__manifest__.py index cd184a7c82..5e18a8cd60 100644 --- a/fieldservice_isp_account/__manifest__.py +++ b/fieldservice_isp_account/__manifest__.py @@ -22,5 +22,9 @@ ], 'license': 'AGPL-3', 'development_status': 'Beta', - 'maintainers': ['osimallen'], + 'maintainers': [ + 'osimallen', + 'brian10048', + 'bodedra', + ], } From 603972c4265cd7172447c7255809f5180d8e6a24 Mon Sep 17 00:00:00 2001 From: oca-travis Date: Thu, 5 Dec 2019 19:00:23 +0000 Subject: [PATCH 04/33] [UPD] Update fieldservice_isp_account.pot --- .../i18n/fieldservice_isp_account.pot | 214 ++++++++++++++++++ 1 file changed, 214 insertions(+) create mode 100644 fieldservice_isp_account/i18n/fieldservice_isp_account.pot diff --git a/fieldservice_isp_account/i18n/fieldservice_isp_account.pot b/fieldservice_isp_account/i18n/fieldservice_isp_account.pot new file mode 100644 index 0000000000..8777744e96 --- /dev/null +++ b/fieldservice_isp_account/i18n/fieldservice_isp_account.pot @@ -0,0 +1,214 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * fieldservice_isp_account +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 12.0\n" +"Report-Msgid-Bugs-To: \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: fieldservice_isp_account +#: model:ir.model.fields,field_description:fieldservice_isp_account.field_fsm_order__account_stage +msgid "Accounting Stage" +msgstr "" + +#. module: fieldservice_isp_account +#: code:addons/fieldservice_isp_account/models/fsm_order.py:68 +#, python-format +msgid "Cannot move to Complete until 'Contractor Costs' is filled in" +msgstr "" + +#. module: fieldservice_isp_account +#: code:addons/fieldservice_isp_account/models/fsm_order.py:71 +#, python-format +msgid "Cannot move to Complete until 'Employee Timesheets' is filled in" +msgstr "" + +#. module: fieldservice_isp_account +#: model_terms:ir.ui.view,arch_db:fieldservice_isp_account.fsm_order_form_isp_account +msgid "Confirm" +msgstr "" + +#. module: fieldservice_isp_account +#: selection:fsm.order,account_stage:0 +msgid "Confirmed" +msgstr "" + +#. module: fieldservice_isp_account +#: code:addons/fieldservice_isp_account/models/fsm_order.py:115 +#, python-format +msgid "Contact empty" +msgstr "" + +#. module: fieldservice_isp_account +#: model:ir.model.fields,field_description:fieldservice_isp_account.field_fsm_order__contractor_total +msgid "Contractor Cost Estimate" +msgstr "" + +#. module: fieldservice_isp_account +#: model:ir.model.fields,field_description:fieldservice_isp_account.field_fsm_order__contractor_cost_ids +#: model_terms:ir.ui.view,arch_db:fieldservice_isp_account.fsm_order_form_isp_account +msgid "Contractor Costs" +msgstr "" + +#. module: fieldservice_isp_account +#: model_terms:ir.ui.view,arch_db:fieldservice_isp_account.fsm_order_form_isp_account +msgid "Create Invoice" +msgstr "" + +#. module: fieldservice_isp_account +#: selection:fsm.order,account_stage:0 +msgid "Draft" +msgstr "" + +#. module: fieldservice_isp_account +#: model_terms:ir.ui.view,arch_db:fieldservice_isp_account.fsm_order_form_isp_account +msgid "Duration" +msgstr "" + +#. module: fieldservice_isp_account +#: model:ir.model.fields,field_description:fieldservice_isp_account.field_fsm_order__employee +msgid "Employee" +msgstr "" + +#. module: fieldservice_isp_account +#: model:ir.model.fields,field_description:fieldservice_isp_account.field_fsm_order__employee_timesheet_ids +#: model_terms:ir.ui.view,arch_db:fieldservice_isp_account.fsm_order_form_isp_account +msgid "Employee Timesheets" +msgstr "" + +#. module: fieldservice_isp_account +#: model:product.template,name:fieldservice_isp_account.field_service_overtime +msgid "FSM Overtime" +msgstr "" + +#. module: fieldservice_isp_account +#: model:product.template,name:fieldservice_isp_account.field_service_regular_time +msgid "FSM Regular Time" +msgstr "" + +#. module: fieldservice_isp_account +#: model:product.template,name:fieldservice_isp_account.field_service_travel_time +msgid "FSM Travel Time" +msgstr "" + +#. module: fieldservice_isp_account +#: model:ir.model,name:fieldservice_isp_account.model_fsm_order +msgid "Field Service Order" +msgstr "" + +#. module: fieldservice_isp_account +#: model:ir.model,name:fieldservice_isp_account.model_fsm_person +msgid "Field Service Worker" +msgstr "" + +#. module: fieldservice_isp_account +#: selection:fsm.order,account_stage:0 +msgid "Fully Invoiced" +msgstr "" + +#. module: fieldservice_isp_account +#: model:product.template,uom_name:fieldservice_isp_account.field_service_overtime +#: model:product.template,uom_name:fieldservice_isp_account.field_service_regular_time +#: model:product.template,uom_name:fieldservice_isp_account.field_service_travel_time +msgid "Hour(s)" +msgstr "" + +#. module: fieldservice_isp_account +#: model:ir.model,name:fieldservice_isp_account.model_account_invoice_line +msgid "Invoice Line" +msgstr "" + +#. module: fieldservice_isp_account +#: selection:fsm.order,account_stage:0 +#: model_terms:ir.ui.view,arch_db:fieldservice_isp_account.fsm_order_search_view +msgid "Needs Review" +msgstr "" + +#. module: fieldservice_isp_account +#: model_terms:ir.actions.act_window,help:fieldservice_isp_account.action_account_fsm_order +msgid "No Field Service Orders to review." +msgstr "" + +#. module: fieldservice_isp_account +#: model_terms:ir.ui.view,arch_db:fieldservice_isp_account.fsm_order_form_isp_account +msgid "No Invoice" +msgstr "" + +#. module: fieldservice_isp_account +#: selection:fsm.order,account_stage:0 +msgid "Nothing Invoiced" +msgstr "" + +#. module: fieldservice_isp_account +#: model:ir.ui.menu,name:fieldservice_isp_account.menu_account_operations +msgid "Operations" +msgstr "" + +#. module: fieldservice_isp_account +#: model:product.template,description_sale:fieldservice_isp_account.field_service_overtime +msgid "Overtime for Field Service Employees\n" +" " +msgstr "" + +#. module: fieldservice_isp_account +#: code:addons/fieldservice_isp_account/models/account_invoice_line.py:19 +#, python-format +msgid "Please set the field service worker." +msgstr "" + +#. module: fieldservice_isp_account +#: model:product.template,description_sale:fieldservice_isp_account.field_service_regular_time +msgid "Regular time for Field Service\n" +" Employees\n" +" " +msgstr "" + +#. module: fieldservice_isp_account +#: model:ir.actions.act_window,name:fieldservice_isp_account.action_account_fsm_order +#: model:ir.ui.menu,name:fieldservice_isp_account.menu_account_fsm_order +msgid "Service Orders" +msgstr "" + +#. module: fieldservice_isp_account +#: code:addons/fieldservice_isp_account/models/fsm_order.py:102 +#, python-format +msgid "The worker assigned to this order is not a supplier" +msgstr "" + +#. module: fieldservice_isp_account +#: model_terms:ir.ui.view,arch_db:fieldservice_isp_account.fsm_order_form_isp_account +msgid "Timesheet Activities" +msgstr "" + +#. module: fieldservice_isp_account +#: model:ir.model.fields,field_description:fieldservice_isp_account.field_fsm_order__employee_time_total +msgid "Total Employee Hours" +msgstr "" + +#. module: fieldservice_isp_account +#: model:product.template,description_sale:fieldservice_isp_account.field_service_travel_time +msgid "Travel time for Field Service\n" +" Employees\n" +" " +msgstr "" + +#. module: fieldservice_isp_account +#: model:ir.model.fields,field_description:fieldservice_isp_account.field_fsm_person__bill_count +#: model_terms:ir.ui.view,arch_db:fieldservice_isp_account.fsm_person_form_isp_account +msgid "Vendor Bills" +msgstr "" + +#. module: fieldservice_isp_account +#: model:product.template,weight_uom_name:fieldservice_isp_account.field_service_overtime +#: model:product.template,weight_uom_name:fieldservice_isp_account.field_service_regular_time +#: model:product.template,weight_uom_name:fieldservice_isp_account.field_service_travel_time +msgid "kg" +msgstr "" + From 2d8b62285963d11e3134d83d91dcb2794d10a484 Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Thu, 5 Dec 2019 19:10:27 +0000 Subject: [PATCH 05/33] [UPD] README.rst --- fieldservice_isp_account/README.rst | 38 ++++++++++--------- .../static/description/index.html | 23 +++++------ 2 files changed, 31 insertions(+), 30 deletions(-) diff --git a/fieldservice_isp_account/README.rst b/fieldservice_isp_account/README.rst index 278e295c55..cb1c58ed6b 100644 --- a/fieldservice_isp_account/README.rst +++ b/fieldservice_isp_account/README.rst @@ -1,6 +1,6 @@ -========================== -Field Service - Accounting -========================== +============================== +Field Service - ISP Accounting +============================== .. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !! This file is generated by oca-gen-addon-readme !! @@ -14,10 +14,10 @@ Field Service - Accounting :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :alt: License: AGPL-3 .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Ffield--service-lightgray.png?logo=github - :target: https://github.com/OCA/field-service/tree/12.0/fieldservice_account + :target: https://github.com/OCA/field-service/tree/12.0/fieldservice_isp_account :alt: OCA/field-service .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png - :target: https://translation.odoo-community.org/projects/field-service-12-0/field-service-12-0-fieldservice_account + :target: https://translation.odoo-community.org/projects/field-service-12-0/field-service-12-0-fieldservice_isp_account :alt: Translate me on Weblate .. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png :target: https://runbot.odoo-community.org/runbot/264/12.0 @@ -43,25 +43,22 @@ No special installation instructions Configuration ============= -To configure this module, you need to: - -* Go to Field Service > Master Data > Locations -* Create or select a location and set their analytic account +No special configuration instructions. Usage ===== To use the module: -On a field service order, open the "Accounting" tab. Depending on -whether the logged in user or an employee or not, they will see +On a field service order, open the "Accounting" tab. Depending on +whether the logged in user or an employee or not, they will see either a place to enter timesheet records or contractor costs. -The total cost of the order is calculated based on the entries in +The total cost of the order is calculated based on the entries in the employee timesheet entries and contractor costs. When an order is completed, a customer invoice will be generated for -the employee time and the contractor costs. A vendor bill will be +the employee time and the contractor costs. A vendor bill will be created for the contractor costs. Bug Tracker @@ -70,7 +67,7 @@ Bug Tracker Bugs are tracked on `GitHub Issues `_. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us smashing it by providing a detailed and welcomed -`feedback `_. +`feedback `_. Do not contact contributors directly about support or help with technical issues. @@ -87,6 +84,7 @@ Contributors * Michael Allen * Serpent Consulting Services Pvt. Ltd. +* Brian McMaster Other credits ~~~~~~~~~~~~~ @@ -111,11 +109,17 @@ promote its widespread use. .. |maintainer-osimallen| image:: https://github.com/osimallen.png?size=40px :target: https://github.com/osimallen :alt: osimallen +.. |maintainer-brian10048| image:: https://github.com/brian10048.png?size=40px + :target: https://github.com/brian10048 + :alt: brian10048 +.. |maintainer-bodedra| image:: https://github.com/bodedra.png?size=40px + :target: https://github.com/bodedra + :alt: bodedra -Current `maintainer `__: +Current `maintainers `__: -|maintainer-osimallen| +|maintainer-osimallen| |maintainer-brian10048| |maintainer-bodedra| -This module is part of the `OCA/field-service `_ project on GitHub. +This module is part of the `OCA/field-service `_ project on GitHub. You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/fieldservice_isp_account/static/description/index.html b/fieldservice_isp_account/static/description/index.html index be45db7518..04cc1226d9 100644 --- a/fieldservice_isp_account/static/description/index.html +++ b/fieldservice_isp_account/static/description/index.html @@ -4,7 +4,7 @@ -Field Service - Accounting +Field Service - ISP Accounting -
-

Field Service - Accounting

+
+

Field Service - ISP Accounting

-

Beta License: AGPL-3 OCA/field-service Translate me on Weblate Try me on Runbot

+

Beta License: AGPL-3 OCA/field-service Translate me on Weblate Try me on Runbot

This module adds the ability to track employee time and contractor costs for Field Service Orders. It also adds functionality to create a customer invoice and a vendor bill when a Field Service Order is @@ -394,11 +394,7 @@

Installation

Configuration

-

To configure this module, you need to:

-
    -
  • Go to Field Service > Master Data > Locations
  • -
  • Create or select a location and set their analytic account
  • -
+

No special configuration instructions.

Usage

@@ -417,7 +413,7 @@

Bug Tracker

Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us smashing it by providing a detailed and welcomed -feedback.

+feedback.

Do not contact contributors directly about support or help with technical issues.

@@ -433,6 +429,7 @@

Contributors

@@ -449,9 +446,9 @@

Maintainers

OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use.

-

Current maintainer:

-

osimallen

-

This module is part of the OCA/field-service project on GitHub.

+

Current maintainers:

+

osimallen brian10048 bodedra

+

This module is part of the OCA/field-service project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

From 1850b3eb870b059f953033e55a704a3c727b0657 Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Thu, 5 Dec 2019 19:10:27 +0000 Subject: [PATCH 06/33] fieldservice_isp_account 12.0.2.0.2 --- fieldservice_isp_account/__manifest__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fieldservice_isp_account/__manifest__.py b/fieldservice_isp_account/__manifest__.py index 5e18a8cd60..e527f23850 100644 --- a/fieldservice_isp_account/__manifest__.py +++ b/fieldservice_isp_account/__manifest__.py @@ -5,7 +5,7 @@ 'name': 'Field Service - ISP Accounting', 'summary': """Invoice Field Service orders based on employee time or contractor costs""", - 'version': '12.0.2.0.1', + 'version': '12.0.2.0.2', 'category': 'Field Service', 'author': 'Open Source Integrators, Odoo Community Association (OCA)', 'website': 'https://github.com/OCA/field-service', From 179f10e2d2687ed8367122796015b0fb6921cadf Mon Sep 17 00:00:00 2001 From: scampbell Date: Wed, 11 Dec 2019 15:17:47 -0800 Subject: [PATCH 07/33] [IMP] Search Company ID --- fieldservice_isp_account/models/fsm_order.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fieldservice_isp_account/models/fsm_order.py b/fieldservice_isp_account/models/fsm_order.py index 4b0637fd24..a203ac7d83 100644 --- a/fieldservice_isp_account/models/fsm_order.py +++ b/fieldservice_isp_account/models/fsm_order.py @@ -84,7 +84,8 @@ def create_bills(self): 'type': 'in_invoice', 'journal_id': jrnl.id or False, 'fiscal_position_id': fpos.id or False, - 'fsm_order_id': self.id + 'fsm_order_id': self.id, + 'company_id': self.env.user.company_id.id } bill = self.env['account.invoice'].sudo().create(vals) for line in self.contractor_cost_ids: @@ -130,7 +131,8 @@ def account_create_invoice(self): 'type': 'out_invoice', 'journal_id': jrnl.id or False, 'fiscal_position_id': fpos.id or False, - 'fsm_order_id': self.id + 'fsm_order_id': self.id, + 'company_id': self.env.user.company_id.id } invoice = self.env['account.invoice'].sudo().create(vals) price_list = invoice.partner_id.property_product_pricelist From ad278f6d286ffff51801076582769e4287ea2dc7 Mon Sep 17 00:00:00 2001 From: oca-travis Date: Thu, 12 Dec 2019 06:50:29 +0000 Subject: [PATCH 08/33] [UPD] Update fieldservice_isp_account.pot --- fieldservice_isp_account/i18n/fieldservice_isp_account.pot | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fieldservice_isp_account/i18n/fieldservice_isp_account.pot b/fieldservice_isp_account/i18n/fieldservice_isp_account.pot index 8777744e96..c633f9becc 100644 --- a/fieldservice_isp_account/i18n/fieldservice_isp_account.pot +++ b/fieldservice_isp_account/i18n/fieldservice_isp_account.pot @@ -41,7 +41,7 @@ msgid "Confirmed" msgstr "" #. module: fieldservice_isp_account -#: code:addons/fieldservice_isp_account/models/fsm_order.py:115 +#: code:addons/fieldservice_isp_account/models/fsm_order.py:116 #, python-format msgid "Contact empty" msgstr "" @@ -177,7 +177,7 @@ msgid "Service Orders" msgstr "" #. module: fieldservice_isp_account -#: code:addons/fieldservice_isp_account/models/fsm_order.py:102 +#: code:addons/fieldservice_isp_account/models/fsm_order.py:103 #, python-format msgid "The worker assigned to this order is not a supplier" msgstr "" From 920edce67ce1222320f6fbd8c56d0cc9724fe00d Mon Sep 17 00:00:00 2001 From: Maxime Chambreuil Date: Fri, 13 Dec 2019 16:11:49 -0600 Subject: [PATCH 09/33] [FIX] fieldservice_isp_account: Show the header at the top --- fieldservice_isp_account/views/fsm_order.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fieldservice_isp_account/views/fsm_order.xml b/fieldservice_isp_account/views/fsm_order.xml index 469e19c4c4..1565e1b6fd 100644 --- a/fieldservice_isp_account/views/fsm_order.xml +++ b/fieldservice_isp_account/views/fsm_order.xml @@ -3,10 +3,10 @@ fsm.order.form.isp.account fsm.order - - +
-
+ From 4ccca713c37daca553af5c6f36e3f3fe3fae9eaa Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Mon, 16 Dec 2019 18:42:45 +0000 Subject: [PATCH 10/33] fieldservice_isp_account 12.0.2.1.0 --- fieldservice_isp_account/__manifest__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fieldservice_isp_account/__manifest__.py b/fieldservice_isp_account/__manifest__.py index e527f23850..1084f8ee4f 100644 --- a/fieldservice_isp_account/__manifest__.py +++ b/fieldservice_isp_account/__manifest__.py @@ -5,7 +5,7 @@ 'name': 'Field Service - ISP Accounting', 'summary': """Invoice Field Service orders based on employee time or contractor costs""", - 'version': '12.0.2.0.2', + 'version': '12.0.2.1.0', 'category': 'Field Service', 'author': 'Open Source Integrators, Odoo Community Association (OCA)', 'website': 'https://github.com/OCA/field-service', From fbdfad0fbe8b2cb061b8da169699c0cad0bde031 Mon Sep 17 00:00:00 2001 From: Maxime Chambreuil Date: Thu, 26 Dec 2019 14:39:09 -0600 Subject: [PATCH 11/33] [IMP] fieldservice_isp_account: m2m between fsm.order and invoice --- fieldservice_isp_account/models/fsm_order.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/fieldservice_isp_account/models/fsm_order.py b/fieldservice_isp_account/models/fsm_order.py index a203ac7d83..6afa1cb878 100644 --- a/fieldservice_isp_account/models/fsm_order.py +++ b/fieldservice_isp_account/models/fsm_order.py @@ -84,7 +84,7 @@ def create_bills(self): 'type': 'in_invoice', 'journal_id': jrnl.id or False, 'fiscal_position_id': fpos.id or False, - 'fsm_order_id': self.id, + 'fsm_order_ids': [(4, self.id)], 'company_id': self.env.user.company_id.id } bill = self.env['account.invoice'].sudo().create(vals) @@ -120,7 +120,7 @@ def account_create_invoice(self): 'type': 'out_invoice', 'journal_id': jrnl.id or False, 'fiscal_position_id': fpos.id or False, - 'fsm_order_id': self.id + 'fsm_order_ids': [(4, self.id)], } invoice = self.env['account.invoice'].sudo().create(vals) price_list = invoice.partner_id.property_product_pricelist @@ -131,7 +131,7 @@ def account_create_invoice(self): 'type': 'out_invoice', 'journal_id': jrnl.id or False, 'fiscal_position_id': fpos.id or False, - 'fsm_order_id': self.id, + 'fsm_order_ids': [(4, self.id)], 'company_id': self.env.user.company_id.id } invoice = self.env['account.invoice'].sudo().create(vals) @@ -152,7 +152,8 @@ def account_create_invoice(self): 'name': line.name, 'price_unit': price, 'account_id': account.id, - 'invoice_id': invoice.id + 'invoice_id': invoice.id, + 'fsm_order_id': self.id, } time_cost = self.env['account.invoice.line'].create(vals) taxes = template.taxes_id @@ -173,7 +174,8 @@ def account_create_invoice(self): 'name': cost.name, 'price_unit': price, 'account_id': account.id, - 'invoice_id': invoice.id + 'invoice_id': invoice.id, + 'fsm_order_id': self.id, } con_cost = self.env['account.invoice.line'].create(vals) taxes = template.taxes_id From 4d32a66c1f29e1d08e72efedd2dba341046711be Mon Sep 17 00:00:00 2001 From: Marcel Savegnago Date: Thu, 16 Jan 2020 06:27:14 +0000 Subject: [PATCH 12/33] Added translation using Weblate (Portuguese (Brazil)) --- fieldservice_isp_account/i18n/pt_BR.po | 214 +++++++++++++++++++++++++ 1 file changed, 214 insertions(+) create mode 100644 fieldservice_isp_account/i18n/pt_BR.po diff --git a/fieldservice_isp_account/i18n/pt_BR.po b/fieldservice_isp_account/i18n/pt_BR.po new file mode 100644 index 0000000000..799dc78b97 --- /dev/null +++ b/fieldservice_isp_account/i18n/pt_BR.po @@ -0,0 +1,214 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * fieldservice_isp_account +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 12.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: Automatically generated\n" +"Language-Team: none\n" +"Language: pt_BR\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n > 1;\n" + +#. module: fieldservice_isp_account +#: model:ir.model.fields,field_description:fieldservice_isp_account.field_fsm_order__account_stage +msgid "Accounting Stage" +msgstr "" + +#. module: fieldservice_isp_account +#: code:addons/fieldservice_isp_account/models/fsm_order.py:68 +#, python-format +msgid "Cannot move to Complete until 'Contractor Costs' is filled in" +msgstr "" + +#. module: fieldservice_isp_account +#: code:addons/fieldservice_isp_account/models/fsm_order.py:71 +#, python-format +msgid "Cannot move to Complete until 'Employee Timesheets' is filled in" +msgstr "" + +#. module: fieldservice_isp_account +#: model_terms:ir.ui.view,arch_db:fieldservice_isp_account.fsm_order_form_isp_account +msgid "Confirm" +msgstr "" + +#. module: fieldservice_isp_account +#: selection:fsm.order,account_stage:0 +msgid "Confirmed" +msgstr "" + +#. module: fieldservice_isp_account +#: code:addons/fieldservice_isp_account/models/fsm_order.py:116 +#, python-format +msgid "Contact empty" +msgstr "" + +#. module: fieldservice_isp_account +#: model:ir.model.fields,field_description:fieldservice_isp_account.field_fsm_order__contractor_total +msgid "Contractor Cost Estimate" +msgstr "" + +#. module: fieldservice_isp_account +#: model:ir.model.fields,field_description:fieldservice_isp_account.field_fsm_order__contractor_cost_ids +#: model_terms:ir.ui.view,arch_db:fieldservice_isp_account.fsm_order_form_isp_account +msgid "Contractor Costs" +msgstr "" + +#. module: fieldservice_isp_account +#: model_terms:ir.ui.view,arch_db:fieldservice_isp_account.fsm_order_form_isp_account +msgid "Create Invoice" +msgstr "" + +#. module: fieldservice_isp_account +#: selection:fsm.order,account_stage:0 +msgid "Draft" +msgstr "" + +#. module: fieldservice_isp_account +#: model_terms:ir.ui.view,arch_db:fieldservice_isp_account.fsm_order_form_isp_account +msgid "Duration" +msgstr "" + +#. module: fieldservice_isp_account +#: model:ir.model.fields,field_description:fieldservice_isp_account.field_fsm_order__employee +msgid "Employee" +msgstr "" + +#. module: fieldservice_isp_account +#: model:ir.model.fields,field_description:fieldservice_isp_account.field_fsm_order__employee_timesheet_ids +#: model_terms:ir.ui.view,arch_db:fieldservice_isp_account.fsm_order_form_isp_account +msgid "Employee Timesheets" +msgstr "" + +#. module: fieldservice_isp_account +#: model:product.template,name:fieldservice_isp_account.field_service_overtime +msgid "FSM Overtime" +msgstr "" + +#. module: fieldservice_isp_account +#: model:product.template,name:fieldservice_isp_account.field_service_regular_time +msgid "FSM Regular Time" +msgstr "" + +#. module: fieldservice_isp_account +#: model:product.template,name:fieldservice_isp_account.field_service_travel_time +msgid "FSM Travel Time" +msgstr "" + +#. module: fieldservice_isp_account +#: model:ir.model,name:fieldservice_isp_account.model_fsm_order +msgid "Field Service Order" +msgstr "" + +#. module: fieldservice_isp_account +#: model:ir.model,name:fieldservice_isp_account.model_fsm_person +msgid "Field Service Worker" +msgstr "" + +#. module: fieldservice_isp_account +#: selection:fsm.order,account_stage:0 +msgid "Fully Invoiced" +msgstr "" + +#. module: fieldservice_isp_account +#: model:product.template,uom_name:fieldservice_isp_account.field_service_overtime +#: model:product.template,uom_name:fieldservice_isp_account.field_service_regular_time +#: model:product.template,uom_name:fieldservice_isp_account.field_service_travel_time +msgid "Hour(s)" +msgstr "" + +#. module: fieldservice_isp_account +#: model:ir.model,name:fieldservice_isp_account.model_account_invoice_line +msgid "Invoice Line" +msgstr "" + +#. module: fieldservice_isp_account +#: selection:fsm.order,account_stage:0 +#: model_terms:ir.ui.view,arch_db:fieldservice_isp_account.fsm_order_search_view +msgid "Needs Review" +msgstr "" + +#. module: fieldservice_isp_account +#: model_terms:ir.actions.act_window,help:fieldservice_isp_account.action_account_fsm_order +msgid "No Field Service Orders to review." +msgstr "" + +#. module: fieldservice_isp_account +#: model_terms:ir.ui.view,arch_db:fieldservice_isp_account.fsm_order_form_isp_account +msgid "No Invoice" +msgstr "" + +#. module: fieldservice_isp_account +#: selection:fsm.order,account_stage:0 +msgid "Nothing Invoiced" +msgstr "" + +#. module: fieldservice_isp_account +#: model:ir.ui.menu,name:fieldservice_isp_account.menu_account_operations +msgid "Operations" +msgstr "" + +#. module: fieldservice_isp_account +#: model:product.template,description_sale:fieldservice_isp_account.field_service_overtime +msgid "Overtime for Field Service Employees\n" +" " +msgstr "" + +#. module: fieldservice_isp_account +#: code:addons/fieldservice_isp_account/models/account_invoice_line.py:19 +#, python-format +msgid "Please set the field service worker." +msgstr "" + +#. module: fieldservice_isp_account +#: model:product.template,description_sale:fieldservice_isp_account.field_service_regular_time +msgid "Regular time for Field Service\n" +" Employees\n" +" " +msgstr "" + +#. module: fieldservice_isp_account +#: model:ir.actions.act_window,name:fieldservice_isp_account.action_account_fsm_order +#: model:ir.ui.menu,name:fieldservice_isp_account.menu_account_fsm_order +msgid "Service Orders" +msgstr "" + +#. module: fieldservice_isp_account +#: code:addons/fieldservice_isp_account/models/fsm_order.py:103 +#, python-format +msgid "The worker assigned to this order is not a supplier" +msgstr "" + +#. module: fieldservice_isp_account +#: model_terms:ir.ui.view,arch_db:fieldservice_isp_account.fsm_order_form_isp_account +msgid "Timesheet Activities" +msgstr "" + +#. module: fieldservice_isp_account +#: model:ir.model.fields,field_description:fieldservice_isp_account.field_fsm_order__employee_time_total +msgid "Total Employee Hours" +msgstr "" + +#. module: fieldservice_isp_account +#: model:product.template,description_sale:fieldservice_isp_account.field_service_travel_time +msgid "Travel time for Field Service\n" +" Employees\n" +" " +msgstr "" + +#. module: fieldservice_isp_account +#: model:ir.model.fields,field_description:fieldservice_isp_account.field_fsm_person__bill_count +#: model_terms:ir.ui.view,arch_db:fieldservice_isp_account.fsm_person_form_isp_account +msgid "Vendor Bills" +msgstr "" + +#. module: fieldservice_isp_account +#: model:product.template,weight_uom_name:fieldservice_isp_account.field_service_overtime +#: model:product.template,weight_uom_name:fieldservice_isp_account.field_service_regular_time +#: model:product.template,weight_uom_name:fieldservice_isp_account.field_service_travel_time +msgid "kg" +msgstr "" From 0f134b78c2570315a3099cf2ca5bc0a5ee16568f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nelson=20Ram=C3=ADrez=20S=C3=A1nchez?= Date: Thu, 16 Jan 2020 22:28:42 +0000 Subject: [PATCH 13/33] Added translation using Weblate (Spanish (Chile)) --- fieldservice_isp_account/i18n/es_CL.po | 214 +++++++++++++++++++++++++ 1 file changed, 214 insertions(+) create mode 100644 fieldservice_isp_account/i18n/es_CL.po diff --git a/fieldservice_isp_account/i18n/es_CL.po b/fieldservice_isp_account/i18n/es_CL.po new file mode 100644 index 0000000000..0748a24e68 --- /dev/null +++ b/fieldservice_isp_account/i18n/es_CL.po @@ -0,0 +1,214 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * fieldservice_isp_account +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 12.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: Automatically generated\n" +"Language-Team: none\n" +"Language: es_CL\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" + +#. module: fieldservice_isp_account +#: model:ir.model.fields,field_description:fieldservice_isp_account.field_fsm_order__account_stage +msgid "Accounting Stage" +msgstr "" + +#. module: fieldservice_isp_account +#: code:addons/fieldservice_isp_account/models/fsm_order.py:68 +#, python-format +msgid "Cannot move to Complete until 'Contractor Costs' is filled in" +msgstr "" + +#. module: fieldservice_isp_account +#: code:addons/fieldservice_isp_account/models/fsm_order.py:71 +#, python-format +msgid "Cannot move to Complete until 'Employee Timesheets' is filled in" +msgstr "" + +#. module: fieldservice_isp_account +#: model_terms:ir.ui.view,arch_db:fieldservice_isp_account.fsm_order_form_isp_account +msgid "Confirm" +msgstr "" + +#. module: fieldservice_isp_account +#: selection:fsm.order,account_stage:0 +msgid "Confirmed" +msgstr "" + +#. module: fieldservice_isp_account +#: code:addons/fieldservice_isp_account/models/fsm_order.py:116 +#, python-format +msgid "Contact empty" +msgstr "" + +#. module: fieldservice_isp_account +#: model:ir.model.fields,field_description:fieldservice_isp_account.field_fsm_order__contractor_total +msgid "Contractor Cost Estimate" +msgstr "" + +#. module: fieldservice_isp_account +#: model:ir.model.fields,field_description:fieldservice_isp_account.field_fsm_order__contractor_cost_ids +#: model_terms:ir.ui.view,arch_db:fieldservice_isp_account.fsm_order_form_isp_account +msgid "Contractor Costs" +msgstr "" + +#. module: fieldservice_isp_account +#: model_terms:ir.ui.view,arch_db:fieldservice_isp_account.fsm_order_form_isp_account +msgid "Create Invoice" +msgstr "" + +#. module: fieldservice_isp_account +#: selection:fsm.order,account_stage:0 +msgid "Draft" +msgstr "" + +#. module: fieldservice_isp_account +#: model_terms:ir.ui.view,arch_db:fieldservice_isp_account.fsm_order_form_isp_account +msgid "Duration" +msgstr "" + +#. module: fieldservice_isp_account +#: model:ir.model.fields,field_description:fieldservice_isp_account.field_fsm_order__employee +msgid "Employee" +msgstr "" + +#. module: fieldservice_isp_account +#: model:ir.model.fields,field_description:fieldservice_isp_account.field_fsm_order__employee_timesheet_ids +#: model_terms:ir.ui.view,arch_db:fieldservice_isp_account.fsm_order_form_isp_account +msgid "Employee Timesheets" +msgstr "" + +#. module: fieldservice_isp_account +#: model:product.template,name:fieldservice_isp_account.field_service_overtime +msgid "FSM Overtime" +msgstr "" + +#. module: fieldservice_isp_account +#: model:product.template,name:fieldservice_isp_account.field_service_regular_time +msgid "FSM Regular Time" +msgstr "" + +#. module: fieldservice_isp_account +#: model:product.template,name:fieldservice_isp_account.field_service_travel_time +msgid "FSM Travel Time" +msgstr "" + +#. module: fieldservice_isp_account +#: model:ir.model,name:fieldservice_isp_account.model_fsm_order +msgid "Field Service Order" +msgstr "" + +#. module: fieldservice_isp_account +#: model:ir.model,name:fieldservice_isp_account.model_fsm_person +msgid "Field Service Worker" +msgstr "" + +#. module: fieldservice_isp_account +#: selection:fsm.order,account_stage:0 +msgid "Fully Invoiced" +msgstr "" + +#. module: fieldservice_isp_account +#: model:product.template,uom_name:fieldservice_isp_account.field_service_overtime +#: model:product.template,uom_name:fieldservice_isp_account.field_service_regular_time +#: model:product.template,uom_name:fieldservice_isp_account.field_service_travel_time +msgid "Hour(s)" +msgstr "" + +#. module: fieldservice_isp_account +#: model:ir.model,name:fieldservice_isp_account.model_account_invoice_line +msgid "Invoice Line" +msgstr "" + +#. module: fieldservice_isp_account +#: selection:fsm.order,account_stage:0 +#: model_terms:ir.ui.view,arch_db:fieldservice_isp_account.fsm_order_search_view +msgid "Needs Review" +msgstr "" + +#. module: fieldservice_isp_account +#: model_terms:ir.actions.act_window,help:fieldservice_isp_account.action_account_fsm_order +msgid "No Field Service Orders to review." +msgstr "" + +#. module: fieldservice_isp_account +#: model_terms:ir.ui.view,arch_db:fieldservice_isp_account.fsm_order_form_isp_account +msgid "No Invoice" +msgstr "" + +#. module: fieldservice_isp_account +#: selection:fsm.order,account_stage:0 +msgid "Nothing Invoiced" +msgstr "" + +#. module: fieldservice_isp_account +#: model:ir.ui.menu,name:fieldservice_isp_account.menu_account_operations +msgid "Operations" +msgstr "" + +#. module: fieldservice_isp_account +#: model:product.template,description_sale:fieldservice_isp_account.field_service_overtime +msgid "Overtime for Field Service Employees\n" +" " +msgstr "" + +#. module: fieldservice_isp_account +#: code:addons/fieldservice_isp_account/models/account_invoice_line.py:19 +#, python-format +msgid "Please set the field service worker." +msgstr "" + +#. module: fieldservice_isp_account +#: model:product.template,description_sale:fieldservice_isp_account.field_service_regular_time +msgid "Regular time for Field Service\n" +" Employees\n" +" " +msgstr "" + +#. module: fieldservice_isp_account +#: model:ir.actions.act_window,name:fieldservice_isp_account.action_account_fsm_order +#: model:ir.ui.menu,name:fieldservice_isp_account.menu_account_fsm_order +msgid "Service Orders" +msgstr "" + +#. module: fieldservice_isp_account +#: code:addons/fieldservice_isp_account/models/fsm_order.py:103 +#, python-format +msgid "The worker assigned to this order is not a supplier" +msgstr "" + +#. module: fieldservice_isp_account +#: model_terms:ir.ui.view,arch_db:fieldservice_isp_account.fsm_order_form_isp_account +msgid "Timesheet Activities" +msgstr "" + +#. module: fieldservice_isp_account +#: model:ir.model.fields,field_description:fieldservice_isp_account.field_fsm_order__employee_time_total +msgid "Total Employee Hours" +msgstr "" + +#. module: fieldservice_isp_account +#: model:product.template,description_sale:fieldservice_isp_account.field_service_travel_time +msgid "Travel time for Field Service\n" +" Employees\n" +" " +msgstr "" + +#. module: fieldservice_isp_account +#: model:ir.model.fields,field_description:fieldservice_isp_account.field_fsm_person__bill_count +#: model_terms:ir.ui.view,arch_db:fieldservice_isp_account.fsm_person_form_isp_account +msgid "Vendor Bills" +msgstr "" + +#. module: fieldservice_isp_account +#: model:product.template,weight_uom_name:fieldservice_isp_account.field_service_overtime +#: model:product.template,weight_uom_name:fieldservice_isp_account.field_service_regular_time +#: model:product.template,weight_uom_name:fieldservice_isp_account.field_service_travel_time +msgid "kg" +msgstr "" From 02da79dcab287ef3a325f064282392827988d101 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nelson=20Ram=C3=ADrez=20S=C3=A1nchez?= Date: Thu, 16 Jan 2020 22:29:04 +0000 Subject: [PATCH 14/33] Translated using Weblate (Spanish (Chile)) Currently translated at 16.7% (6 of 36 strings) Translation: field-service-12.0/field-service-12.0-fieldservice_isp_account Translate-URL: https://translation.odoo-community.org/projects/field-service-12-0/field-service-12-0-fieldservice_isp_account/es_CL/ --- fieldservice_isp_account/i18n/es_CL.po | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/fieldservice_isp_account/i18n/es_CL.po b/fieldservice_isp_account/i18n/es_CL.po index 0748a24e68..8c1be242e8 100644 --- a/fieldservice_isp_account/i18n/es_CL.po +++ b/fieldservice_isp_account/i18n/es_CL.po @@ -6,46 +6,52 @@ msgid "" msgstr "" "Project-Id-Version: Odoo Server 12.0\n" "Report-Msgid-Bugs-To: \n" -"Last-Translator: Automatically generated\n" +"PO-Revision-Date: 2020-01-16 22:38+0000\n" +"Last-Translator: Nelson Ramírez Sánchez \n" "Language-Team: none\n" "Language: es_CL\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: \n" "Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 3.10\n" #. module: fieldservice_isp_account #: model:ir.model.fields,field_description:fieldservice_isp_account.field_fsm_order__account_stage msgid "Accounting Stage" -msgstr "" +msgstr "Etapa Contable" #. module: fieldservice_isp_account #: code:addons/fieldservice_isp_account/models/fsm_order.py:68 #, python-format msgid "Cannot move to Complete until 'Contractor Costs' is filled in" msgstr "" +"No se puede pasar a Completado hasta que se completen los 'Costos del " +"contratista'" #. module: fieldservice_isp_account #: code:addons/fieldservice_isp_account/models/fsm_order.py:71 #, python-format msgid "Cannot move to Complete until 'Employee Timesheets' is filled in" msgstr "" +"No se puede mover a Completado hasta que se completen las 'Hojas de tiempo " +"de empleado'" #. module: fieldservice_isp_account #: model_terms:ir.ui.view,arch_db:fieldservice_isp_account.fsm_order_form_isp_account msgid "Confirm" -msgstr "" +msgstr "Confirmar" #. module: fieldservice_isp_account #: selection:fsm.order,account_stage:0 msgid "Confirmed" -msgstr "" +msgstr "Confirmado" #. module: fieldservice_isp_account #: code:addons/fieldservice_isp_account/models/fsm_order.py:116 #, python-format msgid "Contact empty" -msgstr "" +msgstr "Contacto Vacío" #. module: fieldservice_isp_account #: model:ir.model.fields,field_description:fieldservice_isp_account.field_fsm_order__contractor_total From c8746b776c3d0896a5f7191676aca1d7419e1c48 Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Fri, 17 Jan 2020 01:02:50 +0000 Subject: [PATCH 15/33] fieldservice_isp_account 12.0.2.1.1 --- fieldservice_isp_account/__manifest__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fieldservice_isp_account/__manifest__.py b/fieldservice_isp_account/__manifest__.py index 1084f8ee4f..36f7423042 100644 --- a/fieldservice_isp_account/__manifest__.py +++ b/fieldservice_isp_account/__manifest__.py @@ -5,7 +5,7 @@ 'name': 'Field Service - ISP Accounting', 'summary': """Invoice Field Service orders based on employee time or contractor costs""", - 'version': '12.0.2.1.0', + 'version': '12.0.2.1.1', 'category': 'Field Service', 'author': 'Open Source Integrators, Odoo Community Association (OCA)', 'website': 'https://github.com/OCA/field-service', From a1839191c0e5ec9d6292d0f8b3e6796ff74e93fb Mon Sep 17 00:00:00 2001 From: Marcel Savegnago Date: Mon, 10 Feb 2020 15:26:27 +0000 Subject: [PATCH 16/33] Translated using Weblate (Portuguese (Brazil)) Currently translated at 75.0% (27 of 36 strings) Translation: field-service-12.0/field-service-12.0-fieldservice_isp_account Translate-URL: https://translation.odoo-community.org/projects/field-service-12-0/field-service-12-0-fieldservice_isp_account/pt_BR/ --- fieldservice_isp_account/i18n/pt_BR.po | 58 +++++++++++++------------- 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/fieldservice_isp_account/i18n/pt_BR.po b/fieldservice_isp_account/i18n/pt_BR.po index 799dc78b97..ec32197ce9 100644 --- a/fieldservice_isp_account/i18n/pt_BR.po +++ b/fieldservice_isp_account/i18n/pt_BR.po @@ -6,18 +6,20 @@ msgid "" msgstr "" "Project-Id-Version: Odoo Server 12.0\n" "Report-Msgid-Bugs-To: \n" -"Last-Translator: Automatically generated\n" +"PO-Revision-Date: 2020-02-10 15:33+0000\n" +"Last-Translator: Marcel Savegnago \n" "Language-Team: none\n" "Language: pt_BR\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: \n" "Plural-Forms: nplurals=2; plural=n > 1;\n" +"X-Generator: Weblate 3.10\n" #. module: fieldservice_isp_account #: model:ir.model.fields,field_description:fieldservice_isp_account.field_fsm_order__account_stage msgid "Accounting Stage" -msgstr "" +msgstr "Etapa Contábil" #. module: fieldservice_isp_account #: code:addons/fieldservice_isp_account/models/fsm_order.py:68 @@ -34,49 +36,49 @@ msgstr "" #. module: fieldservice_isp_account #: model_terms:ir.ui.view,arch_db:fieldservice_isp_account.fsm_order_form_isp_account msgid "Confirm" -msgstr "" +msgstr "Confirmar" #. module: fieldservice_isp_account #: selection:fsm.order,account_stage:0 msgid "Confirmed" -msgstr "" +msgstr "Confirmado" #. module: fieldservice_isp_account #: code:addons/fieldservice_isp_account/models/fsm_order.py:116 #, python-format msgid "Contact empty" -msgstr "" +msgstr "Contato Vazio" #. module: fieldservice_isp_account #: model:ir.model.fields,field_description:fieldservice_isp_account.field_fsm_order__contractor_total msgid "Contractor Cost Estimate" -msgstr "" +msgstr "Estimativa de Custos do Contratante" #. module: fieldservice_isp_account #: model:ir.model.fields,field_description:fieldservice_isp_account.field_fsm_order__contractor_cost_ids #: model_terms:ir.ui.view,arch_db:fieldservice_isp_account.fsm_order_form_isp_account msgid "Contractor Costs" -msgstr "" +msgstr "Custos do Contratante" #. module: fieldservice_isp_account #: model_terms:ir.ui.view,arch_db:fieldservice_isp_account.fsm_order_form_isp_account msgid "Create Invoice" -msgstr "" +msgstr "Criar Fatura" #. module: fieldservice_isp_account #: selection:fsm.order,account_stage:0 msgid "Draft" -msgstr "" +msgstr "Provisório" #. module: fieldservice_isp_account #: model_terms:ir.ui.view,arch_db:fieldservice_isp_account.fsm_order_form_isp_account msgid "Duration" -msgstr "" +msgstr "Duração" #. module: fieldservice_isp_account #: model:ir.model.fields,field_description:fieldservice_isp_account.field_fsm_order__employee msgid "Employee" -msgstr "" +msgstr "Funcionario" #. module: fieldservice_isp_account #: model:ir.model.fields,field_description:fieldservice_isp_account.field_fsm_order__employee_timesheet_ids @@ -102,55 +104,55 @@ msgstr "" #. module: fieldservice_isp_account #: model:ir.model,name:fieldservice_isp_account.model_fsm_order msgid "Field Service Order" -msgstr "" +msgstr "Ordem de Serviço de Campo" #. module: fieldservice_isp_account #: model:ir.model,name:fieldservice_isp_account.model_fsm_person msgid "Field Service Worker" -msgstr "" +msgstr "Trabalhador do Serviço de Campo" #. module: fieldservice_isp_account #: selection:fsm.order,account_stage:0 msgid "Fully Invoiced" -msgstr "" +msgstr "Totalmente Faturado" #. module: fieldservice_isp_account #: model:product.template,uom_name:fieldservice_isp_account.field_service_overtime #: model:product.template,uom_name:fieldservice_isp_account.field_service_regular_time #: model:product.template,uom_name:fieldservice_isp_account.field_service_travel_time msgid "Hour(s)" -msgstr "" +msgstr "Hora(s)" #. module: fieldservice_isp_account #: model:ir.model,name:fieldservice_isp_account.model_account_invoice_line msgid "Invoice Line" -msgstr "" +msgstr "Linha de Fatura" #. module: fieldservice_isp_account #: selection:fsm.order,account_stage:0 #: model_terms:ir.ui.view,arch_db:fieldservice_isp_account.fsm_order_search_view msgid "Needs Review" -msgstr "" +msgstr "Necessita de Revisão" #. module: fieldservice_isp_account #: model_terms:ir.actions.act_window,help:fieldservice_isp_account.action_account_fsm_order msgid "No Field Service Orders to review." -msgstr "" +msgstr "Sem Ordens de Serviço de Campo para revisar." #. module: fieldservice_isp_account #: model_terms:ir.ui.view,arch_db:fieldservice_isp_account.fsm_order_form_isp_account msgid "No Invoice" -msgstr "" +msgstr "Sem Fatura" #. module: fieldservice_isp_account #: selection:fsm.order,account_stage:0 msgid "Nothing Invoiced" -msgstr "" +msgstr "Nada Faturado" #. module: fieldservice_isp_account #: model:ir.ui.menu,name:fieldservice_isp_account.menu_account_operations msgid "Operations" -msgstr "" +msgstr "Operações" #. module: fieldservice_isp_account #: model:product.template,description_sale:fieldservice_isp_account.field_service_overtime @@ -162,7 +164,7 @@ msgstr "" #: code:addons/fieldservice_isp_account/models/account_invoice_line.py:19 #, python-format msgid "Please set the field service worker." -msgstr "" +msgstr "Por favor, selecione o trabalhador do serviço de campo." #. module: fieldservice_isp_account #: model:product.template,description_sale:fieldservice_isp_account.field_service_regular_time @@ -175,23 +177,23 @@ msgstr "" #: model:ir.actions.act_window,name:fieldservice_isp_account.action_account_fsm_order #: model:ir.ui.menu,name:fieldservice_isp_account.menu_account_fsm_order msgid "Service Orders" -msgstr "" +msgstr "Ordens de Serviço" #. module: fieldservice_isp_account #: code:addons/fieldservice_isp_account/models/fsm_order.py:103 #, python-format msgid "The worker assigned to this order is not a supplier" -msgstr "" +msgstr "O trabalhador designado para esta ordem não é um fornecedor" #. module: fieldservice_isp_account #: model_terms:ir.ui.view,arch_db:fieldservice_isp_account.fsm_order_form_isp_account msgid "Timesheet Activities" -msgstr "" +msgstr "Atividades da Planilha de Tempo" #. module: fieldservice_isp_account #: model:ir.model.fields,field_description:fieldservice_isp_account.field_fsm_order__employee_time_total msgid "Total Employee Hours" -msgstr "" +msgstr "Carga Horária Total do Empregado" #. module: fieldservice_isp_account #: model:product.template,description_sale:fieldservice_isp_account.field_service_travel_time @@ -204,11 +206,11 @@ msgstr "" #: model:ir.model.fields,field_description:fieldservice_isp_account.field_fsm_person__bill_count #: model_terms:ir.ui.view,arch_db:fieldservice_isp_account.fsm_person_form_isp_account msgid "Vendor Bills" -msgstr "" +msgstr "# Contas de Fornecedores" #. module: fieldservice_isp_account #: model:product.template,weight_uom_name:fieldservice_isp_account.field_service_overtime #: model:product.template,weight_uom_name:fieldservice_isp_account.field_service_regular_time #: model:product.template,weight_uom_name:fieldservice_isp_account.field_service_travel_time msgid "kg" -msgstr "" +msgstr "kg" From 440665b3db92cb28dd016d6f22aee73f0ad72ad1 Mon Sep 17 00:00:00 2001 From: scampbell Date: Thu, 2 Apr 2020 09:18:09 -0700 Subject: [PATCH 17/33] [IMP] Default Set Project and Task [IMP] Add Project + Task to View for Context --- fieldservice_isp_account/__manifest__.py | 1 + fieldservice_isp_account/views/fsm_order.xml | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/fieldservice_isp_account/__manifest__.py b/fieldservice_isp_account/__manifest__.py index 36f7423042..5a511cf3d8 100644 --- a/fieldservice_isp_account/__manifest__.py +++ b/fieldservice_isp_account/__manifest__.py @@ -11,6 +11,7 @@ 'website': 'https://github.com/OCA/field-service', 'depends': [ 'fieldservice_account_analytic', + 'fieldservice_project', 'hr_timesheet', ], 'data': [ diff --git a/fieldservice_isp_account/views/fsm_order.xml b/fieldservice_isp_account/views/fsm_order.xml index 1565e1b6fd..fb57aa3643 100644 --- a/fieldservice_isp_account/views/fsm_order.xml +++ b/fieldservice_isp_account/views/fsm_order.xml @@ -50,7 +50,7 @@ - + @@ -63,6 +63,8 @@ + + From e63fa6bfb95ec4095eb25b7cf6852d0b5712b36b Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Fri, 3 Apr 2020 07:16:05 +0000 Subject: [PATCH 18/33] fieldservice_isp_account 12.0.2.2.0 --- fieldservice_isp_account/__manifest__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fieldservice_isp_account/__manifest__.py b/fieldservice_isp_account/__manifest__.py index 5a511cf3d8..9d92829615 100644 --- a/fieldservice_isp_account/__manifest__.py +++ b/fieldservice_isp_account/__manifest__.py @@ -5,7 +5,7 @@ 'name': 'Field Service - ISP Accounting', 'summary': """Invoice Field Service orders based on employee time or contractor costs""", - 'version': '12.0.2.1.1', + 'version': '12.0.2.2.0', 'category': 'Field Service', 'author': 'Open Source Integrators, Odoo Community Association (OCA)', 'website': 'https://github.com/OCA/field-service', From 33c5121950415c85b420aec3e75f216e41331815 Mon Sep 17 00:00:00 2001 From: Fernando Colus Date: Wed, 17 Jun 2020 13:26:45 +0000 Subject: [PATCH 19/33] Translated using Weblate (Portuguese (Brazil)) Currently translated at 100.0% (36 of 36 strings) Translation: field-service-12.0/field-service-12.0-fieldservice_isp_account Translate-URL: https://translation.odoo-community.org/projects/field-service-12-0/field-service-12-0-fieldservice_isp_account/pt_BR/ --- fieldservice_isp_account/i18n/pt_BR.po | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/fieldservice_isp_account/i18n/pt_BR.po b/fieldservice_isp_account/i18n/pt_BR.po index ec32197ce9..bcf8c1ad34 100644 --- a/fieldservice_isp_account/i18n/pt_BR.po +++ b/fieldservice_isp_account/i18n/pt_BR.po @@ -6,8 +6,8 @@ msgid "" msgstr "" "Project-Id-Version: Odoo Server 12.0\n" "Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: 2020-02-10 15:33+0000\n" -"Last-Translator: Marcel Savegnago \n" +"PO-Revision-Date: 2020-06-17 16:19+0000\n" +"Last-Translator: Fernando Colus \n" "Language-Team: none\n" "Language: pt_BR\n" "MIME-Version: 1.0\n" @@ -26,12 +26,16 @@ msgstr "Etapa Contábil" #, python-format msgid "Cannot move to Complete until 'Contractor Costs' is filled in" msgstr "" +"Não é possível mover para \"Concluído\" até que \"Custos do contratante\" " +"seja preenchido" #. module: fieldservice_isp_account #: code:addons/fieldservice_isp_account/models/fsm_order.py:71 #, python-format msgid "Cannot move to Complete until 'Employee Timesheets' is filled in" msgstr "" +"Não é possível mover para \"Concluído\" até que o \"Quadro de horários dos " +"funcionários\" seja preenchido" #. module: fieldservice_isp_account #: model_terms:ir.ui.view,arch_db:fieldservice_isp_account.fsm_order_form_isp_account @@ -84,22 +88,22 @@ msgstr "Funcionario" #: model:ir.model.fields,field_description:fieldservice_isp_account.field_fsm_order__employee_timesheet_ids #: model_terms:ir.ui.view,arch_db:fieldservice_isp_account.fsm_order_form_isp_account msgid "Employee Timesheets" -msgstr "" +msgstr "Quadro de horários dos funcionários" #. module: fieldservice_isp_account #: model:product.template,name:fieldservice_isp_account.field_service_overtime msgid "FSM Overtime" -msgstr "" +msgstr "Horas Extras do FSM (Field Service Management)" #. module: fieldservice_isp_account #: model:product.template,name:fieldservice_isp_account.field_service_regular_time msgid "FSM Regular Time" -msgstr "" +msgstr "Horário Regular do FSM (Field Service Management)" #. module: fieldservice_isp_account #: model:product.template,name:fieldservice_isp_account.field_service_travel_time msgid "FSM Travel Time" -msgstr "" +msgstr "Tempo de Viagem do FSM (Field Service Management)" #. module: fieldservice_isp_account #: model:ir.model,name:fieldservice_isp_account.model_fsm_order @@ -159,6 +163,8 @@ msgstr "Operações" msgid "Overtime for Field Service Employees\n" " " msgstr "" +"Horas extras para funcionários de serviço de campo\n" +" " #. module: fieldservice_isp_account #: code:addons/fieldservice_isp_account/models/account_invoice_line.py:19 @@ -172,6 +178,8 @@ msgid "Regular time for Field Service\n" " Employees\n" " " msgstr "" +"Horário regular para funcionários do serviço de campo\n" +" " #. module: fieldservice_isp_account #: model:ir.actions.act_window,name:fieldservice_isp_account.action_account_fsm_order @@ -201,6 +209,8 @@ msgid "Travel time for Field Service\n" " Employees\n" " " msgstr "" +"Horário regular para funcionários do serviço de campo\n" +" " #. module: fieldservice_isp_account #: model:ir.model.fields,field_description:fieldservice_isp_account.field_fsm_person__bill_count From f51b5df63538c8a792cae22c044e7028ab97e1f7 Mon Sep 17 00:00:00 2001 From: Ignacio Buioli Date: Tue, 21 Jul 2020 03:33:55 +0000 Subject: [PATCH 20/33] Added translation using Weblate (Spanish (Argentina)) --- fieldservice_isp_account/i18n/es_AR.po | 214 +++++++++++++++++++++++++ 1 file changed, 214 insertions(+) create mode 100644 fieldservice_isp_account/i18n/es_AR.po diff --git a/fieldservice_isp_account/i18n/es_AR.po b/fieldservice_isp_account/i18n/es_AR.po new file mode 100644 index 0000000000..65160f2257 --- /dev/null +++ b/fieldservice_isp_account/i18n/es_AR.po @@ -0,0 +1,214 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * fieldservice_isp_account +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 12.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: Automatically generated\n" +"Language-Team: none\n" +"Language: es_AR\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" + +#. module: fieldservice_isp_account +#: model:ir.model.fields,field_description:fieldservice_isp_account.field_fsm_order__account_stage +msgid "Accounting Stage" +msgstr "" + +#. module: fieldservice_isp_account +#: code:addons/fieldservice_isp_account/models/fsm_order.py:68 +#, python-format +msgid "Cannot move to Complete until 'Contractor Costs' is filled in" +msgstr "" + +#. module: fieldservice_isp_account +#: code:addons/fieldservice_isp_account/models/fsm_order.py:71 +#, python-format +msgid "Cannot move to Complete until 'Employee Timesheets' is filled in" +msgstr "" + +#. module: fieldservice_isp_account +#: model_terms:ir.ui.view,arch_db:fieldservice_isp_account.fsm_order_form_isp_account +msgid "Confirm" +msgstr "" + +#. module: fieldservice_isp_account +#: selection:fsm.order,account_stage:0 +msgid "Confirmed" +msgstr "" + +#. module: fieldservice_isp_account +#: code:addons/fieldservice_isp_account/models/fsm_order.py:116 +#, python-format +msgid "Contact empty" +msgstr "" + +#. module: fieldservice_isp_account +#: model:ir.model.fields,field_description:fieldservice_isp_account.field_fsm_order__contractor_total +msgid "Contractor Cost Estimate" +msgstr "" + +#. module: fieldservice_isp_account +#: model:ir.model.fields,field_description:fieldservice_isp_account.field_fsm_order__contractor_cost_ids +#: model_terms:ir.ui.view,arch_db:fieldservice_isp_account.fsm_order_form_isp_account +msgid "Contractor Costs" +msgstr "" + +#. module: fieldservice_isp_account +#: model_terms:ir.ui.view,arch_db:fieldservice_isp_account.fsm_order_form_isp_account +msgid "Create Invoice" +msgstr "" + +#. module: fieldservice_isp_account +#: selection:fsm.order,account_stage:0 +msgid "Draft" +msgstr "" + +#. module: fieldservice_isp_account +#: model_terms:ir.ui.view,arch_db:fieldservice_isp_account.fsm_order_form_isp_account +msgid "Duration" +msgstr "" + +#. module: fieldservice_isp_account +#: model:ir.model.fields,field_description:fieldservice_isp_account.field_fsm_order__employee +msgid "Employee" +msgstr "" + +#. module: fieldservice_isp_account +#: model:ir.model.fields,field_description:fieldservice_isp_account.field_fsm_order__employee_timesheet_ids +#: model_terms:ir.ui.view,arch_db:fieldservice_isp_account.fsm_order_form_isp_account +msgid "Employee Timesheets" +msgstr "" + +#. module: fieldservice_isp_account +#: model:product.template,name:fieldservice_isp_account.field_service_overtime +msgid "FSM Overtime" +msgstr "" + +#. module: fieldservice_isp_account +#: model:product.template,name:fieldservice_isp_account.field_service_regular_time +msgid "FSM Regular Time" +msgstr "" + +#. module: fieldservice_isp_account +#: model:product.template,name:fieldservice_isp_account.field_service_travel_time +msgid "FSM Travel Time" +msgstr "" + +#. module: fieldservice_isp_account +#: model:ir.model,name:fieldservice_isp_account.model_fsm_order +msgid "Field Service Order" +msgstr "" + +#. module: fieldservice_isp_account +#: model:ir.model,name:fieldservice_isp_account.model_fsm_person +msgid "Field Service Worker" +msgstr "" + +#. module: fieldservice_isp_account +#: selection:fsm.order,account_stage:0 +msgid "Fully Invoiced" +msgstr "" + +#. module: fieldservice_isp_account +#: model:product.template,uom_name:fieldservice_isp_account.field_service_overtime +#: model:product.template,uom_name:fieldservice_isp_account.field_service_regular_time +#: model:product.template,uom_name:fieldservice_isp_account.field_service_travel_time +msgid "Hour(s)" +msgstr "" + +#. module: fieldservice_isp_account +#: model:ir.model,name:fieldservice_isp_account.model_account_invoice_line +msgid "Invoice Line" +msgstr "" + +#. module: fieldservice_isp_account +#: selection:fsm.order,account_stage:0 +#: model_terms:ir.ui.view,arch_db:fieldservice_isp_account.fsm_order_search_view +msgid "Needs Review" +msgstr "" + +#. module: fieldservice_isp_account +#: model_terms:ir.actions.act_window,help:fieldservice_isp_account.action_account_fsm_order +msgid "No Field Service Orders to review." +msgstr "" + +#. module: fieldservice_isp_account +#: model_terms:ir.ui.view,arch_db:fieldservice_isp_account.fsm_order_form_isp_account +msgid "No Invoice" +msgstr "" + +#. module: fieldservice_isp_account +#: selection:fsm.order,account_stage:0 +msgid "Nothing Invoiced" +msgstr "" + +#. module: fieldservice_isp_account +#: model:ir.ui.menu,name:fieldservice_isp_account.menu_account_operations +msgid "Operations" +msgstr "" + +#. module: fieldservice_isp_account +#: model:product.template,description_sale:fieldservice_isp_account.field_service_overtime +msgid "Overtime for Field Service Employees\n" +" " +msgstr "" + +#. module: fieldservice_isp_account +#: code:addons/fieldservice_isp_account/models/account_invoice_line.py:19 +#, python-format +msgid "Please set the field service worker." +msgstr "" + +#. module: fieldservice_isp_account +#: model:product.template,description_sale:fieldservice_isp_account.field_service_regular_time +msgid "Regular time for Field Service\n" +" Employees\n" +" " +msgstr "" + +#. module: fieldservice_isp_account +#: model:ir.actions.act_window,name:fieldservice_isp_account.action_account_fsm_order +#: model:ir.ui.menu,name:fieldservice_isp_account.menu_account_fsm_order +msgid "Service Orders" +msgstr "" + +#. module: fieldservice_isp_account +#: code:addons/fieldservice_isp_account/models/fsm_order.py:103 +#, python-format +msgid "The worker assigned to this order is not a supplier" +msgstr "" + +#. module: fieldservice_isp_account +#: model_terms:ir.ui.view,arch_db:fieldservice_isp_account.fsm_order_form_isp_account +msgid "Timesheet Activities" +msgstr "" + +#. module: fieldservice_isp_account +#: model:ir.model.fields,field_description:fieldservice_isp_account.field_fsm_order__employee_time_total +msgid "Total Employee Hours" +msgstr "" + +#. module: fieldservice_isp_account +#: model:product.template,description_sale:fieldservice_isp_account.field_service_travel_time +msgid "Travel time for Field Service\n" +" Employees\n" +" " +msgstr "" + +#. module: fieldservice_isp_account +#: model:ir.model.fields,field_description:fieldservice_isp_account.field_fsm_person__bill_count +#: model_terms:ir.ui.view,arch_db:fieldservice_isp_account.fsm_person_form_isp_account +msgid "Vendor Bills" +msgstr "" + +#. module: fieldservice_isp_account +#: model:product.template,weight_uom_name:fieldservice_isp_account.field_service_overtime +#: model:product.template,weight_uom_name:fieldservice_isp_account.field_service_regular_time +#: model:product.template,weight_uom_name:fieldservice_isp_account.field_service_travel_time +msgid "kg" +msgstr "" From ad88b5a0c1a98e153a279cf104985df38947866c Mon Sep 17 00:00:00 2001 From: Ignacio Buioli Date: Tue, 21 Jul 2020 03:34:11 +0000 Subject: [PATCH 21/33] Translated using Weblate (Spanish (Argentina)) Currently translated at 100.0% (36 of 36 strings) Translation: field-service-12.0/field-service-12.0-fieldservice_isp_account Translate-URL: https://translation.odoo-community.org/projects/field-service-12-0/field-service-12-0-fieldservice_isp_account/es_AR/ --- fieldservice_isp_account/i18n/es_AR.po | 78 +++++++++++++++----------- 1 file changed, 46 insertions(+), 32 deletions(-) diff --git a/fieldservice_isp_account/i18n/es_AR.po b/fieldservice_isp_account/i18n/es_AR.po index 65160f2257..2c5c799966 100644 --- a/fieldservice_isp_account/i18n/es_AR.po +++ b/fieldservice_isp_account/i18n/es_AR.po @@ -6,163 +6,171 @@ msgid "" msgstr "" "Project-Id-Version: Odoo Server 12.0\n" "Report-Msgid-Bugs-To: \n" -"Last-Translator: Automatically generated\n" +"PO-Revision-Date: 2020-07-21 05:19+0000\n" +"Last-Translator: Ignacio Buioli \n" "Language-Team: none\n" "Language: es_AR\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: \n" "Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 3.10\n" #. module: fieldservice_isp_account #: model:ir.model.fields,field_description:fieldservice_isp_account.field_fsm_order__account_stage msgid "Accounting Stage" -msgstr "" +msgstr "Etapa de Contabilidad" #. module: fieldservice_isp_account #: code:addons/fieldservice_isp_account/models/fsm_order.py:68 #, python-format msgid "Cannot move to Complete until 'Contractor Costs' is filled in" msgstr "" +"No se puede pasar a Completo hasta que se completen los 'Costos del " +"Contratista'" #. module: fieldservice_isp_account #: code:addons/fieldservice_isp_account/models/fsm_order.py:71 #, python-format msgid "Cannot move to Complete until 'Employee Timesheets' is filled in" msgstr "" +"No se puede mover a Completado hasta que se completen el 'Parte de Horas del " +"Empleado'" #. module: fieldservice_isp_account #: model_terms:ir.ui.view,arch_db:fieldservice_isp_account.fsm_order_form_isp_account msgid "Confirm" -msgstr "" +msgstr "Confirmar" #. module: fieldservice_isp_account #: selection:fsm.order,account_stage:0 msgid "Confirmed" -msgstr "" +msgstr "Confirmado" #. module: fieldservice_isp_account #: code:addons/fieldservice_isp_account/models/fsm_order.py:116 #, python-format msgid "Contact empty" -msgstr "" +msgstr "Contacto vacío" #. module: fieldservice_isp_account #: model:ir.model.fields,field_description:fieldservice_isp_account.field_fsm_order__contractor_total msgid "Contractor Cost Estimate" -msgstr "" +msgstr "Estimación de Costos del Contratistas" #. module: fieldservice_isp_account #: model:ir.model.fields,field_description:fieldservice_isp_account.field_fsm_order__contractor_cost_ids #: model_terms:ir.ui.view,arch_db:fieldservice_isp_account.fsm_order_form_isp_account msgid "Contractor Costs" -msgstr "" +msgstr "Costos del Contratistas" #. module: fieldservice_isp_account #: model_terms:ir.ui.view,arch_db:fieldservice_isp_account.fsm_order_form_isp_account msgid "Create Invoice" -msgstr "" +msgstr "Crear Factura" #. module: fieldservice_isp_account #: selection:fsm.order,account_stage:0 msgid "Draft" -msgstr "" +msgstr "Borrador" #. module: fieldservice_isp_account #: model_terms:ir.ui.view,arch_db:fieldservice_isp_account.fsm_order_form_isp_account msgid "Duration" -msgstr "" +msgstr "Duración" #. module: fieldservice_isp_account #: model:ir.model.fields,field_description:fieldservice_isp_account.field_fsm_order__employee msgid "Employee" -msgstr "" +msgstr "Empleado" #. module: fieldservice_isp_account #: model:ir.model.fields,field_description:fieldservice_isp_account.field_fsm_order__employee_timesheet_ids #: model_terms:ir.ui.view,arch_db:fieldservice_isp_account.fsm_order_form_isp_account msgid "Employee Timesheets" -msgstr "" +msgstr "Parte de Horas del Empleado" #. module: fieldservice_isp_account #: model:product.template,name:fieldservice_isp_account.field_service_overtime msgid "FSM Overtime" -msgstr "" +msgstr "Horas Extra FSM" #. module: fieldservice_isp_account #: model:product.template,name:fieldservice_isp_account.field_service_regular_time msgid "FSM Regular Time" -msgstr "" +msgstr "Tiempo Regular FSM" #. module: fieldservice_isp_account #: model:product.template,name:fieldservice_isp_account.field_service_travel_time msgid "FSM Travel Time" -msgstr "" +msgstr "Tiempo de Viaje FSM" #. module: fieldservice_isp_account #: model:ir.model,name:fieldservice_isp_account.model_fsm_order msgid "Field Service Order" -msgstr "" +msgstr "Pedido de Servicio de Campo" #. module: fieldservice_isp_account #: model:ir.model,name:fieldservice_isp_account.model_fsm_person msgid "Field Service Worker" -msgstr "" +msgstr "Trabajador de Servicio de Campo" #. module: fieldservice_isp_account #: selection:fsm.order,account_stage:0 msgid "Fully Invoiced" -msgstr "" +msgstr "Completamente Facturado" #. module: fieldservice_isp_account #: model:product.template,uom_name:fieldservice_isp_account.field_service_overtime #: model:product.template,uom_name:fieldservice_isp_account.field_service_regular_time #: model:product.template,uom_name:fieldservice_isp_account.field_service_travel_time msgid "Hour(s)" -msgstr "" +msgstr "Hora(s)" #. module: fieldservice_isp_account #: model:ir.model,name:fieldservice_isp_account.model_account_invoice_line msgid "Invoice Line" -msgstr "" +msgstr "Línea de Factura" #. module: fieldservice_isp_account #: selection:fsm.order,account_stage:0 #: model_terms:ir.ui.view,arch_db:fieldservice_isp_account.fsm_order_search_view msgid "Needs Review" -msgstr "" +msgstr "Necesita Revisión" #. module: fieldservice_isp_account #: model_terms:ir.actions.act_window,help:fieldservice_isp_account.action_account_fsm_order msgid "No Field Service Orders to review." -msgstr "" +msgstr "No hay Pedidos de Servicio de Campo para revisar." #. module: fieldservice_isp_account #: model_terms:ir.ui.view,arch_db:fieldservice_isp_account.fsm_order_form_isp_account msgid "No Invoice" -msgstr "" +msgstr "No hay Factura" #. module: fieldservice_isp_account #: selection:fsm.order,account_stage:0 msgid "Nothing Invoiced" -msgstr "" +msgstr "Nada que Facturar" #. module: fieldservice_isp_account #: model:ir.ui.menu,name:fieldservice_isp_account.menu_account_operations msgid "Operations" -msgstr "" +msgstr "Operaciones" #. module: fieldservice_isp_account #: model:product.template,description_sale:fieldservice_isp_account.field_service_overtime msgid "Overtime for Field Service Employees\n" " " msgstr "" +"Horas Extra para los Empleados de Servicio de Campo\n" +" " #. module: fieldservice_isp_account #: code:addons/fieldservice_isp_account/models/account_invoice_line.py:19 #, python-format msgid "Please set the field service worker." -msgstr "" +msgstr "Por favor configure el trabajador de servicio de campo." #. module: fieldservice_isp_account #: model:product.template,description_sale:fieldservice_isp_account.field_service_regular_time @@ -170,28 +178,31 @@ msgid "Regular time for Field Service\n" " Employees\n" " " msgstr "" +"Tiempo regular para Empleados de Servicio de\n" +" Campo\n" +" " #. module: fieldservice_isp_account #: model:ir.actions.act_window,name:fieldservice_isp_account.action_account_fsm_order #: model:ir.ui.menu,name:fieldservice_isp_account.menu_account_fsm_order msgid "Service Orders" -msgstr "" +msgstr "Pedidos de Servicio" #. module: fieldservice_isp_account #: code:addons/fieldservice_isp_account/models/fsm_order.py:103 #, python-format msgid "The worker assigned to this order is not a supplier" -msgstr "" +msgstr "El trabajador asignado a este pedido no es un proveedor" #. module: fieldservice_isp_account #: model_terms:ir.ui.view,arch_db:fieldservice_isp_account.fsm_order_form_isp_account msgid "Timesheet Activities" -msgstr "" +msgstr "Actividades de Parte de Horas" #. module: fieldservice_isp_account #: model:ir.model.fields,field_description:fieldservice_isp_account.field_fsm_order__employee_time_total msgid "Total Employee Hours" -msgstr "" +msgstr "Total de Horas de Empleados" #. module: fieldservice_isp_account #: model:product.template,description_sale:fieldservice_isp_account.field_service_travel_time @@ -199,16 +210,19 @@ msgid "Travel time for Field Service\n" " Employees\n" " " msgstr "" +"Tiempo de viaje para Empleados de Servicio de\n" +" Campo\n" +" " #. module: fieldservice_isp_account #: model:ir.model.fields,field_description:fieldservice_isp_account.field_fsm_person__bill_count #: model_terms:ir.ui.view,arch_db:fieldservice_isp_account.fsm_person_form_isp_account msgid "Vendor Bills" -msgstr "" +msgstr "Cuentas de Vendedor" #. module: fieldservice_isp_account #: model:product.template,weight_uom_name:fieldservice_isp_account.field_service_overtime #: model:product.template,weight_uom_name:fieldservice_isp_account.field_service_regular_time #: model:product.template,weight_uom_name:fieldservice_isp_account.field_service_travel_time msgid "kg" -msgstr "" +msgstr "kg" From 492493ce2a1f0a3c081249c87b15990464a7704a Mon Sep 17 00:00:00 2001 From: Murtuza Saleh Date: Mon, 24 Aug 2020 23:02:23 +0530 Subject: [PATCH 22/33] [IMP] Gave Access to portal user. --- fieldservice_isp_account/__manifest__.py | 1 + fieldservice_isp_account/security/ir.model.access.csv | 2 ++ 2 files changed, 3 insertions(+) create mode 100644 fieldservice_isp_account/security/ir.model.access.csv diff --git a/fieldservice_isp_account/__manifest__.py b/fieldservice_isp_account/__manifest__.py index 9d92829615..f870512525 100644 --- a/fieldservice_isp_account/__manifest__.py +++ b/fieldservice_isp_account/__manifest__.py @@ -15,6 +15,7 @@ 'hr_timesheet', ], 'data': [ + 'security/ir.model.access.csv', 'data/time_products.xml', 'views/account.xml', 'views/fsm_order.xml', diff --git a/fieldservice_isp_account/security/ir.model.access.csv b/fieldservice_isp_account/security/ir.model.access.csv new file mode 100644 index 0000000000..8da6f134f1 --- /dev/null +++ b/fieldservice_isp_account/security/ir.model.access.csv @@ -0,0 +1,2 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_hr_employee_portal,access.hr.employee.portal,hr.model_hr_employee,base.group_portal,1,0,0,0 From 91a7b1661868ebc80b06be9fdc2b6e79db39b0fa Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Mon, 24 Aug 2020 18:26:20 +0000 Subject: [PATCH 23/33] fieldservice_isp_account 12.0.2.3.0 --- fieldservice_isp_account/__manifest__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fieldservice_isp_account/__manifest__.py b/fieldservice_isp_account/__manifest__.py index f870512525..d03e863709 100644 --- a/fieldservice_isp_account/__manifest__.py +++ b/fieldservice_isp_account/__manifest__.py @@ -5,7 +5,7 @@ 'name': 'Field Service - ISP Accounting', 'summary': """Invoice Field Service orders based on employee time or contractor costs""", - 'version': '12.0.2.2.0', + 'version': '12.0.2.3.0', 'category': 'Field Service', 'author': 'Open Source Integrators, Odoo Community Association (OCA)', 'website': 'https://github.com/OCA/field-service', From d1f94e57b8c457829820aa81f5ff98a44b18e2f4 Mon Sep 17 00:00:00 2001 From: scampbell Date: Thu, 10 Sep 2020 16:15:12 -0700 Subject: [PATCH 24/33] [FIX] FSM Invoice Double Timesheet Entry --- fieldservice_isp_account/models/fsm_order.py | 40 ++++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/fieldservice_isp_account/models/fsm_order.py b/fieldservice_isp_account/models/fsm_order.py index 6afa1cb878..8c30f292b5 100644 --- a/fieldservice_isp_account/models/fsm_order.py +++ b/fieldservice_isp_account/models/fsm_order.py @@ -136,50 +136,50 @@ def account_create_invoice(self): } invoice = self.env['account.invoice'].sudo().create(vals) price_list = invoice.partner_id.property_product_pricelist - for line in self.employee_timesheet_ids: - price = price_list.get_product_price(product=line.product_id, - quantity=line.unit_amount, + for cost in self.contractor_cost_ids: + price = price_list.get_product_price(product=cost.product_id, + quantity=cost.quantity, partner=invoice.partner_id, date=False, uom_id=False) - template = line.product_id.product_tmpl_id + template = cost.product_id.product_tmpl_id accounts = template.get_product_accounts() account = accounts['income'] vals = { - 'product_id': line.product_id.id, - 'account_analytic_id': line.account_id.id, - 'quantity': line.unit_amount, - 'name': line.name, + 'product_id': cost.product_id.id, + 'account_analytic_id': cost.account_analytic_id.id, + 'quantity': cost.quantity, + 'name': cost.name, 'price_unit': price, 'account_id': account.id, 'invoice_id': invoice.id, 'fsm_order_id': self.id, } - time_cost = self.env['account.invoice.line'].create(vals) + con_cost = self.env['account.invoice.line'].create(vals) taxes = template.taxes_id - time_cost.invoice_line_tax_ids = fpos.map_tax(taxes) - for cost in self.contractor_cost_ids: - price = price_list.get_product_price(product=cost.product_id, - quantity=cost.quantity, + con_cost.invoice_line_tax_ids = fpos.map_tax(taxes) + for line in self.employee_timesheet_ids: + price = price_list.get_product_price(product=line.product_id, + quantity=line.unit_amount, partner=invoice.partner_id, date=False, uom_id=False) - template = cost.product_id.product_tmpl_id + template = line.product_id.product_tmpl_id accounts = template.get_product_accounts() account = accounts['income'] vals = { - 'product_id': cost.product_id.id, - 'account_analytic_id': cost.account_analytic_id.id, - 'quantity': cost.quantity, - 'name': cost.name, + 'product_id': line.product_id.id, + 'account_analytic_id': line.account_id.id, + 'quantity': line.unit_amount, + 'name': line.name, 'price_unit': price, 'account_id': account.id, 'invoice_id': invoice.id, 'fsm_order_id': self.id, } - con_cost = self.env['account.invoice.line'].create(vals) + time_cost = self.env['account.invoice.line'].create(vals) taxes = template.taxes_id - con_cost.invoice_line_tax_ids = fpos.map_tax(taxes) + time_cost.invoice_line_tax_ids = fpos.map_tax(taxes) invoice.compute_taxes() self.account_stage = 'invoiced' return invoice From 5c22dad5afcc014aa4ff4aaaa07e49b30daa7c3c Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Mon, 21 Sep 2020 18:13:18 +0000 Subject: [PATCH 25/33] fieldservice_isp_account 12.0.2.3.1 --- fieldservice_isp_account/__manifest__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fieldservice_isp_account/__manifest__.py b/fieldservice_isp_account/__manifest__.py index d03e863709..c9cb774058 100644 --- a/fieldservice_isp_account/__manifest__.py +++ b/fieldservice_isp_account/__manifest__.py @@ -5,7 +5,7 @@ 'name': 'Field Service - ISP Accounting', 'summary': """Invoice Field Service orders based on employee time or contractor costs""", - 'version': '12.0.2.3.0', + 'version': '12.0.2.3.1', 'category': 'Field Service', 'author': 'Open Source Integrators, Odoo Community Association (OCA)', 'website': 'https://github.com/OCA/field-service', From c1573f1ac4df12481f67b2480cad9d5744795df0 Mon Sep 17 00:00:00 2001 From: scampbell Date: Mon, 5 Oct 2020 11:04:28 -0700 Subject: [PATCH 26/33] [IMP] No Update Data .xml --- fieldservice_isp_account/data/time_products.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fieldservice_isp_account/data/time_products.xml b/fieldservice_isp_account/data/time_products.xml index 96a1a4bee3..1ae004dc90 100644 --- a/fieldservice_isp_account/data/time_products.xml +++ b/fieldservice_isp_account/data/time_products.xml @@ -1,5 +1,5 @@ - + FSM Travel Time From 5bb3a711c6f24428727ec8647dbf791d202cf82d Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Mon, 5 Oct 2020 18:58:10 +0000 Subject: [PATCH 27/33] fieldservice_isp_account 12.0.2.3.2 --- fieldservice_isp_account/__manifest__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fieldservice_isp_account/__manifest__.py b/fieldservice_isp_account/__manifest__.py index c9cb774058..16e18a2c70 100644 --- a/fieldservice_isp_account/__manifest__.py +++ b/fieldservice_isp_account/__manifest__.py @@ -5,7 +5,7 @@ 'name': 'Field Service - ISP Accounting', 'summary': """Invoice Field Service orders based on employee time or contractor costs""", - 'version': '12.0.2.3.1', + 'version': '12.0.2.3.2', 'category': 'Field Service', 'author': 'Open Source Integrators, Odoo Community Association (OCA)', 'website': 'https://github.com/OCA/field-service', From 960ed0b6cd5796e68e19239652f62a00fc5f5cb4 Mon Sep 17 00:00:00 2001 From: Benoit Aimont Date: Wed, 28 Apr 2021 09:41:22 +0200 Subject: [PATCH 28/33] [IMP] fieldservice_isp_account: black, isort, prettier --- fieldservice_isp_account/__manifest__.py | 44 ++-- .../data/time_products.xml | 24 +- .../models/account_invoice_line.py | 35 +-- fieldservice_isp_account/models/fsm_order.py | 214 ++++++++++-------- fieldservice_isp_account/models/fsm_person.py | 24 +- .../tests/test_isp_account.py | 191 +++++++++------- fieldservice_isp_account/views/account.xml | 29 ++- fieldservice_isp_account/views/fsm_order.xml | 137 ++++++----- fieldservice_isp_account/views/fsm_person.xml | 19 +- .../views/hr_timesheet.xml | 55 ++--- .../odoo/addons/fieldservice_isp_account | 1 + setup/fieldservice_isp_account/setup.py | 6 + 12 files changed, 425 insertions(+), 354 deletions(-) create mode 120000 setup/fieldservice_isp_account/odoo/addons/fieldservice_isp_account create mode 100644 setup/fieldservice_isp_account/setup.py diff --git a/fieldservice_isp_account/__manifest__.py b/fieldservice_isp_account/__manifest__.py index 16e18a2c70..55cc326f83 100644 --- a/fieldservice_isp_account/__manifest__.py +++ b/fieldservice_isp_account/__manifest__.py @@ -2,31 +2,27 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). { - 'name': 'Field Service - ISP Accounting', - 'summary': """Invoice Field Service orders based on employee time + "name": "Field Service - ISP Accounting", + "summary": """Invoice Field Service orders based on employee time or contractor costs""", - 'version': '12.0.2.3.2', - 'category': 'Field Service', - 'author': 'Open Source Integrators, Odoo Community Association (OCA)', - 'website': 'https://github.com/OCA/field-service', - 'depends': [ - 'fieldservice_account_analytic', - 'fieldservice_project', - 'hr_timesheet', + "version": "12.0.2.3.2", + "category": "Field Service", + "author": "Open Source Integrators, Odoo Community Association (OCA)", + "website": "https://github.com/OCA/field-service", + "depends": [ + "fieldservice_account_analytic", + "fieldservice_project", + "hr_timesheet", ], - 'data': [ - 'security/ir.model.access.csv', - 'data/time_products.xml', - 'views/account.xml', - 'views/fsm_order.xml', - 'views/fsm_person.xml', - 'views/hr_timesheet.xml', - ], - 'license': 'AGPL-3', - 'development_status': 'Beta', - 'maintainers': [ - 'osimallen', - 'brian10048', - 'bodedra', + "data": [ + "security/ir.model.access.csv", + "data/time_products.xml", + "views/account.xml", + "views/fsm_order.xml", + "views/fsm_person.xml", + "views/hr_timesheet.xml", ], + "license": "AGPL-3", + "development_status": "Beta", + "maintainers": ["osimallen", "brian10048", "bodedra"], } diff --git a/fieldservice_isp_account/data/time_products.xml b/fieldservice_isp_account/data/time_products.xml index 1ae004dc90..27fb994a25 100644 --- a/fieldservice_isp_account/data/time_products.xml +++ b/fieldservice_isp_account/data/time_products.xml @@ -1,36 +1,32 @@ - + - FSM Travel Time - + service - - + + Travel time for Field Service Employees - FSM Regular Time - + service - - + + Regular time for Field Service Employees - FSM Overtime - + service - - + + Overtime for Field Service Employees - diff --git a/fieldservice_isp_account/models/account_invoice_line.py b/fieldservice_isp_account/models/account_invoice_line.py index 1f64c10bcb..9a59be9fc0 100644 --- a/fieldservice_isp_account/models/account_invoice_line.py +++ b/fieldservice_isp_account/models/account_invoice_line.py @@ -1,34 +1,39 @@ # Copyright (C) 2018 - TODAY, Open Source Integrators # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import api, models, _ +from odoo import _, api, models from odoo.exceptions import ValidationError class AccountInvoiceLine(models.Model): _inherit = "account.invoice.line" - @api.onchange('product_id', 'quantity') + @api.onchange("product_id", "quantity") def onchange_product_id(self): for line in self: if line.fsm_order_id: - partner = line.fsm_order_id.person_id and\ - line.fsm_order_id.person_id.partner_id or False + partner = ( + line.fsm_order_id.person_id + and line.fsm_order_id.person_id.partner_id + or False + ) if not partner: - raise ValidationError( - _("Please set the field service worker.")) + raise ValidationError(_("Please set the field service worker.")) fpos = partner.property_account_position_id tmpl = line.product_id.product_tmpl_id if line.product_id: accounts = tmpl.get_product_accounts() - supinfo = self.env['product.supplierinfo'].search( - [('name', '=', partner.id), - ('product_tmpl_id', '=', tmpl.id), - ('min_qty', '<=', line.quantity)], - order='min_qty DESC') - line.price_unit = \ + supinfo = self.env["product.supplierinfo"].search( + [ + ("name", "=", partner.id), + ("product_tmpl_id", "=", tmpl.id), + ("min_qty", "<=", line.quantity), + ], + order="min_qty DESC", + ) + line.price_unit = ( supinfo and supinfo[0].price or tmpl.standard_price - line.account_id = accounts.get('expense', False) - line.invoice_line_tax_ids = fpos.\ - map_tax(tmpl.supplier_taxes_id) + ) + line.account_id = accounts.get("expense", False) + line.invoice_line_tax_ids = fpos.map_tax(tmpl.supplier_taxes_id) line.name = line.product_id.name diff --git a/fieldservice_isp_account/models/fsm_order.py b/fieldservice_isp_account/models/fsm_order.py index 8c30f292b5..81c3af3aa1 100644 --- a/fieldservice_isp_account/models/fsm_order.py +++ b/fieldservice_isp_account/models/fsm_order.py @@ -1,41 +1,45 @@ # Copyright (C) 2018 - TODAY, Open Source Integrators # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import api, fields, models, _ +from odoo import _, api, fields, models from odoo.exceptions import ValidationError - -ACCOUNT_STAGES = [('draft', 'Draft'), - ('review', 'Needs Review'), - ('confirmed', 'Confirmed'), - ('invoiced', 'Fully Invoiced'), - ('no', 'Nothing Invoiced')] +ACCOUNT_STAGES = [ + ("draft", "Draft"), + ("review", "Needs Review"), + ("confirmed", "Confirmed"), + ("invoiced", "Fully Invoiced"), + ("no", "Nothing Invoiced"), +] class FSMOrder(models.Model): - _inherit = 'fsm.order' + _inherit = "fsm.order" - contractor_cost_ids = fields.One2many('account.invoice.line', - 'fsm_order_id', - string='Contractor Costs') - employee_timesheet_ids = fields.One2many('account.analytic.line', - "fsm_order_id", - string='Employee Timesheets') - employee = fields.Boolean(compute='_compute_employee') - contractor_total = fields.Float(compute='_compute_contractor_cost', - string='Contractor Cost Estimate') - employee_time_total = fields.Float(compute='_compute_employee_hours', - string='Total Employee Hours') - account_stage = fields.Selection(ACCOUNT_STAGES, string='Accounting Stage', - default='draft') + contractor_cost_ids = fields.One2many( + "account.invoice.line", "fsm_order_id", string="Contractor Costs" + ) + employee_timesheet_ids = fields.One2many( + "account.analytic.line", "fsm_order_id", string="Employee Timesheets" + ) + employee = fields.Boolean(compute="_compute_employee") + contractor_total = fields.Float( + compute="_compute_contractor_cost", string="Contractor Cost Estimate" + ) + employee_time_total = fields.Float( + compute="_compute_employee_hours", string="Total Employee Hours" + ) + account_stage = fields.Selection( + ACCOUNT_STAGES, string="Accounting Stage", default="draft" + ) def _compute_employee(self): - user = self.env['res.users'].browse(self.env.uid) + user = self.env["res.users"].browse(self.env.uid) for order in self: if user.employee_ids: order.employee = True - @api.depends('employee_timesheet_ids', 'contractor_cost_ids') + @api.depends("employee_timesheet_ids", "contractor_cost_ids") def _compute_total_cost(self): super()._compute_total_cost() for order in self: @@ -47,14 +51,14 @@ def _compute_total_cost(self): for cost in order.contractor_cost_ids: order.total_cost += cost.price_unit * cost.quantity - @api.depends('employee_timesheet_ids') + @api.depends("employee_timesheet_ids") def _compute_employee_hours(self): for order in self: order.employee_time_total = 0.0 for line in order.employee_timesheet_ids: order.employee_time_total += line.unit_amount - @api.depends('contractor_cost_ids') + @api.depends("contractor_cost_ids") def _compute_contractor_cost(self): for order in self: order.contractor_total = 0.0 @@ -63,31 +67,39 @@ def _compute_contractor_cost(self): def action_complete(self): for order in self: - order.account_stage = 'review' + order.account_stage = "review" if self.person_id.supplier and not self.contractor_cost_ids: - raise ValidationError(_("Cannot move to Complete " + - "until 'Contractor Costs' is filled in")) + raise ValidationError( + _("Cannot move to Complete " + "until 'Contractor Costs' is filled in") + ) if not self.person_id.supplier and not self.employee_timesheet_ids: - raise ValidationError(_("Cannot move to Complete until " + - "'Employee Timesheets' is filled in")) + raise ValidationError( + _( + "Cannot move to Complete until " + + "'Employee Timesheets' is filled in" + ) + ) return super(FSMOrder, self).action_complete() def create_bills(self): - jrnl = self.env['account.journal'].search([ - ('company_id', '=', self.env.user.company_id.id), - ('type', '=', 'purchase'), - ('active', '=', True)], - limit=1) + jrnl = self.env["account.journal"].search( + [ + ("company_id", "=", self.env.user.company_id.id), + ("type", "=", "purchase"), + ("active", "=", True), + ], + limit=1, + ) fpos = self.customer_id.property_account_position_id vals = { - 'partner_id': self.person_id.partner_id.id, - 'type': 'in_invoice', - 'journal_id': jrnl.id or False, - 'fiscal_position_id': fpos.id or False, - 'fsm_order_ids': [(4, self.id)], - 'company_id': self.env.user.company_id.id + "partner_id": self.person_id.partner_id.id, + "type": "in_invoice", + "journal_id": jrnl.id or False, + "fiscal_position_id": fpos.id or False, + "fsm_order_ids": [(4, self.id)], + "company_id": self.env.user.company_id.id, } - bill = self.env['account.invoice'].sudo().create(vals) + bill = self.env["account.invoice"].sudo().create(vals) for line in self.contractor_cost_ids: line.invoice_id = bill bill.compute_taxes() @@ -98,91 +110,99 @@ def account_confirm(self): if order.contractor_cost_ids: if contractor: order.create_bills() - order.account_stage = 'confirmed' + order.account_stage = "confirmed" else: - raise ValidationError(_("The worker assigned to this order" - " is not a supplier")) + raise ValidationError( + _("The worker assigned to this order" " is not a supplier") + ) if order.employee_timesheet_ids: - order.account_stage = 'confirmed' + order.account_stage = "confirmed" def account_create_invoice(self): - jrnl = self.env['account.journal'].search([ - ('company_id', '=', self.env.user.company_id.id), - ('type', '=', 'sale'), - ('active', '=', True)], - limit=1) - if self.bill_to == 'contact': + jrnl = self.env["account.journal"].search( + [ + ("company_id", "=", self.env.user.company_id.id), + ("type", "=", "sale"), + ("active", "=", True), + ], + limit=1, + ) + if self.bill_to == "contact": if not self.customer_id: raise ValidationError(_("Contact empty")) fpos = self.customer_id.property_account_position_id vals = { - 'partner_id': self.customer_id.id, - 'type': 'out_invoice', - 'journal_id': jrnl.id or False, - 'fiscal_position_id': fpos.id or False, - 'fsm_order_ids': [(4, self.id)], + "partner_id": self.customer_id.id, + "type": "out_invoice", + "journal_id": jrnl.id or False, + "fiscal_position_id": fpos.id or False, + "fsm_order_ids": [(4, self.id)], } - invoice = self.env['account.invoice'].sudo().create(vals) + invoice = self.env["account.invoice"].sudo().create(vals) price_list = invoice.partner_id.property_product_pricelist else: fpos = self.location_id.customer_id.property_account_position_id vals = { - 'partner_id': self.location_id.customer_id.id, - 'type': 'out_invoice', - 'journal_id': jrnl.id or False, - 'fiscal_position_id': fpos.id or False, - 'fsm_order_ids': [(4, self.id)], - 'company_id': self.env.user.company_id.id + "partner_id": self.location_id.customer_id.id, + "type": "out_invoice", + "journal_id": jrnl.id or False, + "fiscal_position_id": fpos.id or False, + "fsm_order_ids": [(4, self.id)], + "company_id": self.env.user.company_id.id, } - invoice = self.env['account.invoice'].sudo().create(vals) + invoice = self.env["account.invoice"].sudo().create(vals) price_list = invoice.partner_id.property_product_pricelist for cost in self.contractor_cost_ids: - price = price_list.get_product_price(product=cost.product_id, - quantity=cost.quantity, - partner=invoice.partner_id, - date=False, - uom_id=False) + price = price_list.get_product_price( + product=cost.product_id, + quantity=cost.quantity, + partner=invoice.partner_id, + date=False, + uom_id=False, + ) template = cost.product_id.product_tmpl_id accounts = template.get_product_accounts() - account = accounts['income'] + account = accounts["income"] vals = { - 'product_id': cost.product_id.id, - 'account_analytic_id': cost.account_analytic_id.id, - 'quantity': cost.quantity, - 'name': cost.name, - 'price_unit': price, - 'account_id': account.id, - 'invoice_id': invoice.id, - 'fsm_order_id': self.id, + "product_id": cost.product_id.id, + "account_analytic_id": cost.account_analytic_id.id, + "quantity": cost.quantity, + "name": cost.name, + "price_unit": price, + "account_id": account.id, + "invoice_id": invoice.id, + "fsm_order_id": self.id, } - con_cost = self.env['account.invoice.line'].create(vals) + con_cost = self.env["account.invoice.line"].create(vals) taxes = template.taxes_id con_cost.invoice_line_tax_ids = fpos.map_tax(taxes) for line in self.employee_timesheet_ids: - price = price_list.get_product_price(product=line.product_id, - quantity=line.unit_amount, - partner=invoice.partner_id, - date=False, - uom_id=False) + price = price_list.get_product_price( + product=line.product_id, + quantity=line.unit_amount, + partner=invoice.partner_id, + date=False, + uom_id=False, + ) template = line.product_id.product_tmpl_id accounts = template.get_product_accounts() - account = accounts['income'] + account = accounts["income"] vals = { - 'product_id': line.product_id.id, - 'account_analytic_id': line.account_id.id, - 'quantity': line.unit_amount, - 'name': line.name, - 'price_unit': price, - 'account_id': account.id, - 'invoice_id': invoice.id, - 'fsm_order_id': self.id, + "product_id": line.product_id.id, + "account_analytic_id": line.account_id.id, + "quantity": line.unit_amount, + "name": line.name, + "price_unit": price, + "account_id": account.id, + "invoice_id": invoice.id, + "fsm_order_id": self.id, } - time_cost = self.env['account.invoice.line'].create(vals) + time_cost = self.env["account.invoice.line"].create(vals) taxes = template.taxes_id time_cost.invoice_line_tax_ids = fpos.map_tax(taxes) invoice.compute_taxes() - self.account_stage = 'invoiced' + self.account_stage = "invoiced" return invoice def account_no_invoice(self): - self.account_stage = 'no' + self.account_stage = "no" diff --git a/fieldservice_isp_account/models/fsm_person.py b/fieldservice_isp_account/models/fsm_person.py index 1b75acfaa6..49ea2a9cc7 100644 --- a/fieldservice_isp_account/models/fsm_person.py +++ b/fieldservice_isp_account/models/fsm_person.py @@ -5,25 +5,25 @@ class FSMPerson(models.Model): - _inherit = 'fsm.person' + _inherit = "fsm.person" - bill_count = fields.Integer(string='Vendor Bills', - compute='_compute_vendor_bills') + bill_count = fields.Integer(string="Vendor Bills", compute="_compute_vendor_bills") def _compute_vendor_bills(self): - self.bill_count = self.env['account.invoice'].search_count([ - ('partner_id', '=', self.partner_id.id)]) + self.bill_count = self.env["account.invoice"].search_count( + [("partner_id", "=", self.partner_id.id)] + ) @api.multi def action_view_bills(self): for bill in self: - action = self.env.ref('account.action_invoice_tree1').read()[0] - vendor_bills = self.env['account.invoice'].search( - [('partner_id', '=', bill.partner_id.id)]) + action = self.env.ref("account.action_invoice_tree1").read()[0] + vendor_bills = self.env["account.invoice"].search( + [("partner_id", "=", bill.partner_id.id)] + ) if len(vendor_bills) == 1: - action['views'] = [ - (self.env.ref('account.invoice_form').id, 'form')] - action['res_id'] = vendor_bills.id + action["views"] = [(self.env.ref("account.invoice_form").id, "form")] + action["res_id"] = vendor_bills.id else: - action['domain'] = [('id', 'in', vendor_bills.ids)] + action["domain"] = [("id", "in", vendor_bills.ids)] return action diff --git a/fieldservice_isp_account/tests/test_isp_account.py b/fieldservice_isp_account/tests/test_isp_account.py index a12cc2de8d..8347ca24a8 100644 --- a/fieldservice_isp_account/tests/test_isp_account.py +++ b/fieldservice_isp_account/tests/test_isp_account.py @@ -2,39 +2,39 @@ # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) from odoo import fields -from odoo.addons.fieldservice_account.tests.test_fsm_account import ( - FSMAccountCase -) from odoo.exceptions import ValidationError +from odoo.addons.fieldservice_account.tests.test_fsm_account import FSMAccountCase -class FSMAccountCase(FSMAccountCase): +class FSMAccountCase(FSMAccountCase): def setUp(self): super(FSMAccountCase, self).setUp() - self.test_person = self.env['fsm.person'].create({'name': 'Worker-1'}) - self.test_analytic = self.env.ref('analytic.analytic_administratif') - self.account_id = self.env['account.account'].create({ - 'code': 'NC1110', - 'name': 'Test Payable Account', - 'user_type_id': - self.env.ref('account.data_account_type_payable').id, - 'reconcile': True - }) + self.test_person = self.env["fsm.person"].create({"name": "Worker-1"}) + self.test_analytic = self.env.ref("analytic.analytic_administratif") + self.account_id = self.env["account.account"].create( + { + "code": "NC1110", + "name": "Test Payable Account", + "user_type_id": self.env.ref("account.data_account_type_payable").id, + "reconcile": True, + } + ) def _create_workorder(self, bill_to, contractors, timesheets): # Create a new work order - contractors = self.env['account.invoice.line'].create(contractors) - timesheets = self.env['account.analytic.line'].create(timesheets) + contractors = self.env["account.invoice.line"].create(contractors) + timesheets = self.env["account.analytic.line"].create(timesheets) - order = self.env['fsm.order'].\ - create({ - 'location_id': self.test_location.id, - 'bill_to': bill_to, - 'person_id': self.test_person.id, - 'contractor_cost_ids': [(6, 0, contractors.ids)], - 'employee_timesheet_ids': [(6, 0, timesheets.ids)] - }) + order = self.env["fsm.order"].create( + { + "location_id": self.test_location.id, + "bill_to": bill_to, + "person_id": self.test_person.id, + "contractor_cost_ids": [(6, 0, contractors.ids)], + "employee_timesheet_ids": [(6, 0, timesheets.ids)], + } + ) order.person_ids += self.test_person return order @@ -42,38 +42,40 @@ def _process_order_to_invoices(self, order): # Change states order.date_start = fields.Datetime.today() order.date_end = fields.Datetime.today() - order.resolution = 'Done something!' + order.resolution = "Done something!" order.action_complete() - self.assertEqual(order.account_stage, 'review') + self.assertEqual(order.account_stage, "review") # Create vendor bill # Vendor bill created from order's contractor if not order.person_id.partner_id.supplier: with self.assertRaises(ValidationError) as e: order.account_confirm() self.assertEqual( - e.exception.name, - "The worker assigned to this order is not a supplier") + e.exception.name, "The worker assigned to this order is not a supplier" + ) order.person_id.partner_id.supplier = True order.account_confirm() - self.assertEqual(order.account_stage, 'confirmed') - bill = self.AccountInvoice.search([('type', '=', 'in_invoice'), - ('fsm_order_id', '=', order.id)]) + self.assertEqual(order.account_stage, "confirmed") + bill = self.AccountInvoice.search( + [("type", "=", "in_invoice"), ("fsm_order_id", "=", order.id)] + ) self.assertEqual(len(bill), 1) - self.assertEqual(len(order.contractor_cost_ids), - len(bill.invoice_line_ids)) + self.assertEqual(len(order.contractor_cost_ids), len(bill.invoice_line_ids)) # Customer invoice created from order's contractor and timehsheet - if order.bill_to == 'contact' and not order.customer_id: + if order.bill_to == "contact" and not order.customer_id: with self.assertRaises(ValidationError): order.account_create_invoice() order.customer_id = self.test_loc_partner # Assign some partner order.account_create_invoice() - self.assertEqual(order.account_stage, 'invoiced') - invoice = self.AccountInvoice.search([('type', '=', 'out_invoice'), - ('fsm_order_id', '=', order.id)]) + self.assertEqual(order.account_stage, "invoiced") + invoice = self.AccountInvoice.search( + [("type", "=", "out_invoice"), ("fsm_order_id", "=", order.id)] + ) self.assertEqual(len(invoice), 1) self.assertEqual( len(order.contractor_cost_ids) + len(order.employee_timesheet_ids), - len(invoice.invoice_line_ids)) + len(invoice.invoice_line_ids), + ) return (bill, invoice) def test_fsm_order_exception(self): @@ -86,23 +88,22 @@ def test_fsm_order_exception(self): # Setup required data self.test_location.analytic_account_id = self.test_analytic # Create a new work order with contract = 500 and timesheet = 300 - self.env.ref('hr.employee_qdp').timesheet_cost = 20.0 - order = self.env['fsm.order'].\ - create({ - 'location_id': self.test_location.id, - 'person_id': self.test_person.id, - }) + self.env.ref("hr.employee_qdp").timesheet_cost = 20.0 + order = self.env["fsm.order"].create( + {"location_id": self.test_location.id, "person_id": self.test_person.id} + ) order.person_id = self.test_person order.person_ids += self.test_person order.date_start = fields.Datetime.today() order.date_end = fields.Datetime.today() - order.resolution = 'Done something!' + order.resolution = "Done something!" with self.assertRaises(ValidationError) as e: order.action_complete() - self.assertEqual(e.exception.name, - "Cannot move to Complete until " - "'Employee Timesheets' is filled in") + self.assertEqual( + e.exception.name, + "Cannot move to Complete until " "'Employee Timesheets' is filled in", + ) def test_fsm_order_bill_to_location(self): """Bill To Location, @@ -110,25 +111,35 @@ def test_fsm_order_bill_to_location(self): """ # Setup required data self.test_location.analytic_account_id = self.test_analytic - contractors = [{'name': 'contractor_line_1', - 'product_id': self.env.ref('product.expense_hotel').id, - 'account_id': self.account_id.id, - 'quantity': 2, - 'price_unit': 200}, ] - self.env.ref('hr.employee_qdp').timesheet_cost = 100 - timesheets = [{'name': 'timesheet_line_1', - 'employee_id': self.env.ref('hr.employee_qdp').id, - 'account_id': self.test_analytic.id, - 'product_id': self.env.ref('product.expense_hotel').id, - 'unit_amount': 6}, - {'name': 'timesheet_line_2', - 'employee_id': self.env.ref('hr.employee_qdp').id, - 'account_id': self.test_analytic.id, - 'product_id': self.env.ref('product.expense_hotel').id, - 'unit_amount': 4}, ] - order = self._create_workorder(bill_to='location', - contractors=contractors, - timesheets=timesheets) + contractors = [ + { + "name": "contractor_line_1", + "product_id": self.env.ref("product.expense_hotel").id, + "account_id": self.account_id.id, + "quantity": 2, + "price_unit": 200, + }, + ] + self.env.ref("hr.employee_qdp").timesheet_cost = 100 + timesheets = [ + { + "name": "timesheet_line_1", + "employee_id": self.env.ref("hr.employee_qdp").id, + "account_id": self.test_analytic.id, + "product_id": self.env.ref("product.expense_hotel").id, + "unit_amount": 6, + }, + { + "name": "timesheet_line_2", + "employee_id": self.env.ref("hr.employee_qdp").id, + "account_id": self.test_analytic.id, + "product_id": self.env.ref("product.expense_hotel").id, + "unit_amount": 4, + }, + ] + order = self._create_workorder( + bill_to="location", contractors=contractors, timesheets=timesheets + ) order._compute_contractor_cost() order._compute_employee_hours() order._compute_total_cost() @@ -147,25 +158,35 @@ def test_fsm_order_bill_to_contact(self): # Setup required data self.test_location.analytic_account_id = self.test_analytic # Create a new work order with contract = 500 and timesheet = 300 - contractors = [{'name': 'contractor_line_2', - 'product_id': self.env.ref('product.expense_hotel').id, - 'account_id': self.account_id.id, - 'quantity': 2, - 'price_unit': 100}, - {'name': 'contractor_line_3', - 'product_id': self.env.ref('product.expense_hotel').id, - 'account_id': self.account_id.id, - 'quantity': 1, - 'price_unit': 300}, ] - self.env.ref('hr.employee_qdp').timesheet_cost = 20.0 - timesheets = [{'name': 'timesheet_line_3', - 'employee_id': self.env.ref('hr.employee_qdp').id, - 'account_id': self.test_analytic.id, - 'product_id': self.env.ref('product.expense_hotel').id, - 'unit_amount': 10}, ] - order = self._create_workorder(bill_to='contact', - contractors=contractors, - timesheets=timesheets) + contractors = [ + { + "name": "contractor_line_2", + "product_id": self.env.ref("product.expense_hotel").id, + "account_id": self.account_id.id, + "quantity": 2, + "price_unit": 100, + }, + { + "name": "contractor_line_3", + "product_id": self.env.ref("product.expense_hotel").id, + "account_id": self.account_id.id, + "quantity": 1, + "price_unit": 300, + }, + ] + self.env.ref("hr.employee_qdp").timesheet_cost = 20.0 + timesheets = [ + { + "name": "timesheet_line_3", + "employee_id": self.env.ref("hr.employee_qdp").id, + "account_id": self.test_analytic.id, + "product_id": self.env.ref("product.expense_hotel").id, + "unit_amount": 10, + }, + ] + order = self._create_workorder( + bill_to="contact", contractors=contractors, timesheets=timesheets + ) order._compute_contractor_cost() order._compute_employee_hours() order._compute_total_cost() diff --git a/fieldservice_isp_account/views/account.xml b/fieldservice_isp_account/views/account.xml index 3e7aeb49e5..20ab239899 100644 --- a/fieldservice_isp_account/views/account.xml +++ b/fieldservice_isp_account/views/account.xml @@ -1,29 +1,28 @@ - Service Orders fsm.order form tree,form - +

No Field Service Orders to review.

- - - - - + +
diff --git a/fieldservice_isp_account/views/fsm_order.xml b/fieldservice_isp_account/views/fsm_order.xml index fb57aa3643..2563e4e60c 100644 --- a/fieldservice_isp_account/views/fsm_order.xml +++ b/fieldservice_isp_account/views/fsm_order.xml @@ -1,92 +1,117 @@ - fsm.order.form.isp.account fsm.order - +
-
- - - - - - - + + + + + + + - + - - - - - - - - - - - + + + + + + + + + + + - - + +
- fsm.order.search fsm.order - + - + -
diff --git a/fieldservice_isp_account/views/fsm_person.xml b/fieldservice_isp_account/views/fsm_person.xml index 0f2a42a1a5..37f0116cae 100644 --- a/fieldservice_isp_account/views/fsm_person.xml +++ b/fieldservice_isp_account/views/fsm_person.xml @@ -1,21 +1,20 @@ - fsm.person.form.isp.account fsm.person - +
-
-
diff --git a/fieldservice_isp_account/views/hr_timesheet.xml b/fieldservice_isp_account/views/hr_timesheet.xml index 25ebf8b849..e49a277dae 100644 --- a/fieldservice_isp_account/views/hr_timesheet.xml +++ b/fieldservice_isp_account/views/hr_timesheet.xml @@ -1,36 +1,39 @@ - - My Timesheets - account.analytic.line - tree,form - [('user_id', '=', uid)] - { + My Timesheets + account.analytic.line + tree,form + [('user_id', '=', uid)] + { "search_default_week":1, } - - -

+ + +

Record a new activity -

+

+

You can register and track your workings hours by project every day. Every time spent on a project will become a cost and can be re-invoiced to customers if required.

-
-
- - - - account.analytic.line.timesheet.user - - - [('user_id', '=', user.id), ('project_id', '!=', False)] - - - - - - - +
+
+ + + account.analytic.line.timesheet.user + + + [('user_id', '=', user.id), ('project_id', '!=', False)] + + + + + + diff --git a/setup/fieldservice_isp_account/odoo/addons/fieldservice_isp_account b/setup/fieldservice_isp_account/odoo/addons/fieldservice_isp_account new file mode 120000 index 0000000000..9914338c7c --- /dev/null +++ b/setup/fieldservice_isp_account/odoo/addons/fieldservice_isp_account @@ -0,0 +1 @@ +../../../../fieldservice_isp_account \ No newline at end of file diff --git a/setup/fieldservice_isp_account/setup.py b/setup/fieldservice_isp_account/setup.py new file mode 100644 index 0000000000..28c57bb640 --- /dev/null +++ b/setup/fieldservice_isp_account/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) From 28bdae523f1930ae052cb1521d1b884cee6f2245 Mon Sep 17 00:00:00 2001 From: Benoit Aimont Date: Wed, 28 Apr 2021 09:57:44 +0200 Subject: [PATCH 29/33] [MIG] fieldservice_isp_account: Migration to 13.0 --- fieldservice_isp_account/README.rst | 14 +- fieldservice_isp_account/__manifest__.py | 3 +- fieldservice_isp_account/i18n/es_AR.po | 6 +- fieldservice_isp_account/i18n/es_CL.po | 6 +- .../i18n/fieldservice_isp_account.pot | 6 +- fieldservice_isp_account/i18n/pt_BR.po | 6 +- fieldservice_isp_account/models/__init__.py | 2 +- .../models/account_invoice_line.py | 39 ------ fieldservice_isp_account/models/fsm_order.py | 131 ++++++++++++------ .../models/fsm_order_cost.py | 24 ++++ fieldservice_isp_account/models/fsm_person.py | 9 +- .../security/fsm_order_cost.xml | 23 +++ .../static/description/index.html | 6 +- .../tests/test_isp_account.py | 69 +++++---- fieldservice_isp_account/views/account.xml | 1 - fieldservice_isp_account/views/fsm_order.xml | 22 ++- 16 files changed, 209 insertions(+), 158 deletions(-) delete mode 100644 fieldservice_isp_account/models/account_invoice_line.py create mode 100644 fieldservice_isp_account/models/fsm_order_cost.py create mode 100644 fieldservice_isp_account/security/fsm_order_cost.xml diff --git a/fieldservice_isp_account/README.rst b/fieldservice_isp_account/README.rst index cb1c58ed6b..5b7501afeb 100644 --- a/fieldservice_isp_account/README.rst +++ b/fieldservice_isp_account/README.rst @@ -14,16 +14,16 @@ Field Service - ISP Accounting :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :alt: License: AGPL-3 .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Ffield--service-lightgray.png?logo=github - :target: https://github.com/OCA/field-service/tree/12.0/fieldservice_isp_account + :target: https://github.com/OCA/field-service/tree/13.0/fieldservice_isp_account :alt: OCA/field-service .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png - :target: https://translation.odoo-community.org/projects/field-service-12-0/field-service-12-0-fieldservice_isp_account + :target: https://translation.odoo-community.org/projects/field-service-13-0/field-service-13-0-fieldservice_isp_account :alt: Translate me on Weblate .. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png - :target: https://runbot.odoo-community.org/runbot/264/12.0 + :target: https://runbot.odoo-community.org/runbot/264/13.0 :alt: Try me on Runbot -|badge1| |badge2| |badge3| |badge4| |badge5| +|badge1| |badge2| |badge3| |badge4| |badge5| This module adds the ability to track employee time and contractor costs for Field Service Orders. It also adds functionality to create @@ -67,7 +67,7 @@ Bug Tracker Bugs are tracked on `GitHub Issues `_. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us smashing it by providing a detailed and welcomed -`feedback `_. +`feedback `_. Do not contact contributors directly about support or help with technical issues. @@ -118,8 +118,8 @@ promote its widespread use. Current `maintainers `__: -|maintainer-osimallen| |maintainer-brian10048| |maintainer-bodedra| +|maintainer-osimallen| |maintainer-brian10048| |maintainer-bodedra| -This module is part of the `OCA/field-service `_ project on GitHub. +This module is part of the `OCA/field-service `_ project on GitHub. You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/fieldservice_isp_account/__manifest__.py b/fieldservice_isp_account/__manifest__.py index 55cc326f83..3c0f211449 100644 --- a/fieldservice_isp_account/__manifest__.py +++ b/fieldservice_isp_account/__manifest__.py @@ -5,7 +5,7 @@ "name": "Field Service - ISP Accounting", "summary": """Invoice Field Service orders based on employee time or contractor costs""", - "version": "12.0.2.3.2", + "version": "13.0.1.0.0", "category": "Field Service", "author": "Open Source Integrators, Odoo Community Association (OCA)", "website": "https://github.com/OCA/field-service", @@ -15,6 +15,7 @@ "hr_timesheet", ], "data": [ + "security/fsm_order_cost.xml", "security/ir.model.access.csv", "data/time_products.xml", "views/account.xml", diff --git a/fieldservice_isp_account/i18n/es_AR.po b/fieldservice_isp_account/i18n/es_AR.po index 2c5c799966..32b7a899ee 100644 --- a/fieldservice_isp_account/i18n/es_AR.po +++ b/fieldservice_isp_account/i18n/es_AR.po @@ -4,7 +4,7 @@ # msgid "" msgstr "" -"Project-Id-Version: Odoo Server 12.0\n" +"Project-Id-Version: Odoo Server 13.0\n" "Report-Msgid-Bugs-To: \n" "PO-Revision-Date: 2020-07-21 05:19+0000\n" "Last-Translator: Ignacio Buioli \n" @@ -128,7 +128,7 @@ msgid "Hour(s)" msgstr "Hora(s)" #. module: fieldservice_isp_account -#: model:ir.model,name:fieldservice_isp_account.model_account_invoice_line +#: model:ir.model,name:fieldservice_isp_account.model_account_move_line msgid "Invoice Line" msgstr "Línea de Factura" @@ -167,7 +167,7 @@ msgstr "" " " #. module: fieldservice_isp_account -#: code:addons/fieldservice_isp_account/models/account_invoice_line.py:19 +#: code:addons/fieldservice_isp_account/models/account_move_line.py:19 #, python-format msgid "Please set the field service worker." msgstr "Por favor configure el trabajador de servicio de campo." diff --git a/fieldservice_isp_account/i18n/es_CL.po b/fieldservice_isp_account/i18n/es_CL.po index 8c1be242e8..25b3c15534 100644 --- a/fieldservice_isp_account/i18n/es_CL.po +++ b/fieldservice_isp_account/i18n/es_CL.po @@ -4,7 +4,7 @@ # msgid "" msgstr "" -"Project-Id-Version: Odoo Server 12.0\n" +"Project-Id-Version: Odoo Server 13.0\n" "Report-Msgid-Bugs-To: \n" "PO-Revision-Date: 2020-01-16 22:38+0000\n" "Last-Translator: Nelson Ramírez Sánchez \n" @@ -128,7 +128,7 @@ msgid "Hour(s)" msgstr "" #. module: fieldservice_isp_account -#: model:ir.model,name:fieldservice_isp_account.model_account_invoice_line +#: model:ir.model,name:fieldservice_isp_account.model_account_move_line msgid "Invoice Line" msgstr "" @@ -165,7 +165,7 @@ msgid "Overtime for Field Service Employees\n" msgstr "" #. module: fieldservice_isp_account -#: code:addons/fieldservice_isp_account/models/account_invoice_line.py:19 +#: code:addons/fieldservice_isp_account/models/account_move_line.py:19 #, python-format msgid "Please set the field service worker." msgstr "" diff --git a/fieldservice_isp_account/i18n/fieldservice_isp_account.pot b/fieldservice_isp_account/i18n/fieldservice_isp_account.pot index c633f9becc..8c87b45cf6 100644 --- a/fieldservice_isp_account/i18n/fieldservice_isp_account.pot +++ b/fieldservice_isp_account/i18n/fieldservice_isp_account.pot @@ -4,7 +4,7 @@ # msgid "" msgstr "" -"Project-Id-Version: Odoo Server 12.0\n" +"Project-Id-Version: Odoo Server 13.0\n" "Report-Msgid-Bugs-To: \n" "Last-Translator: <>\n" "Language-Team: \n" @@ -121,7 +121,7 @@ msgid "Hour(s)" msgstr "" #. module: fieldservice_isp_account -#: model:ir.model,name:fieldservice_isp_account.model_account_invoice_line +#: model:ir.model,name:fieldservice_isp_account.model_account_move_line msgid "Invoice Line" msgstr "" @@ -158,7 +158,7 @@ msgid "Overtime for Field Service Employees\n" msgstr "" #. module: fieldservice_isp_account -#: code:addons/fieldservice_isp_account/models/account_invoice_line.py:19 +#: code:addons/fieldservice_isp_account/models/account_move_line.py:19 #, python-format msgid "Please set the field service worker." msgstr "" diff --git a/fieldservice_isp_account/i18n/pt_BR.po b/fieldservice_isp_account/i18n/pt_BR.po index bcf8c1ad34..907151e7dc 100644 --- a/fieldservice_isp_account/i18n/pt_BR.po +++ b/fieldservice_isp_account/i18n/pt_BR.po @@ -4,7 +4,7 @@ # msgid "" msgstr "" -"Project-Id-Version: Odoo Server 12.0\n" +"Project-Id-Version: Odoo Server 13.0\n" "Report-Msgid-Bugs-To: \n" "PO-Revision-Date: 2020-06-17 16:19+0000\n" "Last-Translator: Fernando Colus \n" @@ -128,7 +128,7 @@ msgid "Hour(s)" msgstr "Hora(s)" #. module: fieldservice_isp_account -#: model:ir.model,name:fieldservice_isp_account.model_account_invoice_line +#: model:ir.model,name:fieldservice_isp_account.model_account_move_line msgid "Invoice Line" msgstr "Linha de Fatura" @@ -167,7 +167,7 @@ msgstr "" " " #. module: fieldservice_isp_account -#: code:addons/fieldservice_isp_account/models/account_invoice_line.py:19 +#: code:addons/fieldservice_isp_account/models/account_move_line.py:19 #, python-format msgid "Please set the field service worker." msgstr "Por favor, selecione o trabalhador do serviço de campo." diff --git a/fieldservice_isp_account/models/__init__.py b/fieldservice_isp_account/models/__init__.py index 19e859522b..a2be44257e 100644 --- a/fieldservice_isp_account/models/__init__.py +++ b/fieldservice_isp_account/models/__init__.py @@ -1,7 +1,7 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from . import ( - account_invoice_line, fsm_order, fsm_person, ) +from . import fsm_order_cost diff --git a/fieldservice_isp_account/models/account_invoice_line.py b/fieldservice_isp_account/models/account_invoice_line.py deleted file mode 100644 index 9a59be9fc0..0000000000 --- a/fieldservice_isp_account/models/account_invoice_line.py +++ /dev/null @@ -1,39 +0,0 @@ -# Copyright (C) 2018 - TODAY, Open Source Integrators -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -from odoo import _, api, models -from odoo.exceptions import ValidationError - - -class AccountInvoiceLine(models.Model): - _inherit = "account.invoice.line" - - @api.onchange("product_id", "quantity") - def onchange_product_id(self): - for line in self: - if line.fsm_order_id: - partner = ( - line.fsm_order_id.person_id - and line.fsm_order_id.person_id.partner_id - or False - ) - if not partner: - raise ValidationError(_("Please set the field service worker.")) - fpos = partner.property_account_position_id - tmpl = line.product_id.product_tmpl_id - if line.product_id: - accounts = tmpl.get_product_accounts() - supinfo = self.env["product.supplierinfo"].search( - [ - ("name", "=", partner.id), - ("product_tmpl_id", "=", tmpl.id), - ("min_qty", "<=", line.quantity), - ], - order="min_qty DESC", - ) - line.price_unit = ( - supinfo and supinfo[0].price or tmpl.standard_price - ) - line.account_id = accounts.get("expense", False) - line.invoice_line_tax_ids = fpos.map_tax(tmpl.supplier_taxes_id) - line.name = line.product_id.name diff --git a/fieldservice_isp_account/models/fsm_order.py b/fieldservice_isp_account/models/fsm_order.py index 81c3af3aa1..68762b024e 100644 --- a/fieldservice_isp_account/models/fsm_order.py +++ b/fieldservice_isp_account/models/fsm_order.py @@ -17,7 +17,7 @@ class FSMOrder(models.Model): _inherit = "fsm.order" contractor_cost_ids = fields.One2many( - "account.invoice.line", "fsm_order_id", string="Contractor Costs" + "fsm.order.cost", "fsm_order_id", string="Contractor Costs" ) employee_timesheet_ids = fields.One2many( "account.analytic.line", "fsm_order_id", string="Employee Timesheets" @@ -38,6 +38,8 @@ def _compute_employee(self): for order in self: if user.employee_ids: order.employee = True + else: + order.employee = False @api.depends("employee_timesheet_ids", "contractor_cost_ids") def _compute_total_cost(self): @@ -68,11 +70,11 @@ def _compute_contractor_cost(self): def action_complete(self): for order in self: order.account_stage = "review" - if self.person_id.supplier and not self.contractor_cost_ids: + if self.person_id.supplier_rank and not self.contractor_cost_ids: raise ValidationError( _("Cannot move to Complete " + "until 'Contractor Costs' is filled in") ) - if not self.person_id.supplier and not self.employee_timesheet_ids: + if not self.person_id.supplier_rank and not self.employee_timesheet_ids: raise ValidationError( _( "Cannot move to Complete until " @@ -84,29 +86,51 @@ def action_complete(self): def create_bills(self): jrnl = self.env["account.journal"].search( [ - ("company_id", "=", self.env.user.company_id.id), + ("company_id", "=", self.env.company.id), ("type", "=", "purchase"), ("active", "=", True), ], limit=1, ) - fpos = self.customer_id.property_account_position_id + fpos = self.person_id.partner_id.property_account_position_id + invoice_line_vals = [] + for cost in self.contractor_cost_ids: + template = cost.product_id.product_tmpl_id + accounts = template.get_product_accounts() + account = accounts["expense"] + taxes = template.supplier_taxes_id + tax_ids = fpos.map_tax(taxes) + invoice_line_vals.append( + ( + 0, + 0, + { + "analytic_account_id": self.location_id.analytic_account_id.id, + "product_id": cost.product_id.id, + "quantity": cost.quantity, + "name": cost.product_id.display_name, + "price_unit": cost.price_unit, + "account_id": account.id, + "fsm_order_ids": [(4, self.id)], + "tax_ids": [(6, 0, tax_ids.ids)], + }, + ) + ) vals = { "partner_id": self.person_id.partner_id.id, "type": "in_invoice", "journal_id": jrnl.id or False, "fiscal_position_id": fpos.id or False, "fsm_order_ids": [(4, self.id)], - "company_id": self.env.user.company_id.id, + "company_id": self.env.company.id, + "invoice_line_ids": invoice_line_vals, } - bill = self.env["account.invoice"].sudo().create(vals) - for line in self.contractor_cost_ids: - line.invoice_id = bill - bill.compute_taxes() + bill = self.env["account.move"].sudo().create(vals) + bill._recompute_tax_lines() def account_confirm(self): for order in self: - contractor = order.person_id.partner_id.supplier + contractor = order.person_id.partner_id.supplier_rank if order.contractor_cost_ids: if contractor: order.create_bills() @@ -121,7 +145,7 @@ def account_confirm(self): def account_create_invoice(self): jrnl = self.env["account.journal"].search( [ - ("company_id", "=", self.env.user.company_id.id), + ("company_id", "=", self.env.company.id), ("type", "=", "sale"), ("active", "=", True), ], @@ -131,76 +155,91 @@ def account_create_invoice(self): if not self.customer_id: raise ValidationError(_("Contact empty")) fpos = self.customer_id.property_account_position_id - vals = { + invoice_vals = { "partner_id": self.customer_id.id, "type": "out_invoice", "journal_id": jrnl.id or False, "fiscal_position_id": fpos.id or False, "fsm_order_ids": [(4, self.id)], } - invoice = self.env["account.invoice"].sudo().create(vals) - price_list = invoice.partner_id.property_product_pricelist + price_list = self.customer_id.property_product_pricelist + else: fpos = self.location_id.customer_id.property_account_position_id - vals = { + invoice_vals = { "partner_id": self.location_id.customer_id.id, "type": "out_invoice", "journal_id": jrnl.id or False, "fiscal_position_id": fpos.id or False, "fsm_order_ids": [(4, self.id)], - "company_id": self.env.user.company_id.id, + "company_id": self.env.company.id, } - invoice = self.env["account.invoice"].sudo().create(vals) - price_list = invoice.partner_id.property_product_pricelist + price_list = self.location_id.customer_id.property_product_pricelist + + invoice_line_vals = [] for cost in self.contractor_cost_ids: price = price_list.get_product_price( product=cost.product_id, quantity=cost.quantity, - partner=invoice.partner_id, + partner=invoice_vals.get("partner_id"), date=False, uom_id=False, ) template = cost.product_id.product_tmpl_id accounts = template.get_product_accounts() account = accounts["income"] - vals = { - "product_id": cost.product_id.id, - "account_analytic_id": cost.account_analytic_id.id, - "quantity": cost.quantity, - "name": cost.name, - "price_unit": price, - "account_id": account.id, - "invoice_id": invoice.id, - "fsm_order_id": self.id, - } - con_cost = self.env["account.invoice.line"].create(vals) taxes = template.taxes_id - con_cost.invoice_line_tax_ids = fpos.map_tax(taxes) + tax_ids = fpos.map_tax(taxes) + invoice_line_vals.append( + ( + 0, + 0, + { + "product_id": cost.product_id.id, + "analytic_account_id": self.location_id.analytic_account_id.id, + "quantity": cost.quantity, + "name": cost.product_id.display_name, + "price_unit": price, + "account_id": account.id, + "fsm_order_ids": [(4, self.id)], + "tax_ids": [(6, 0, tax_ids.ids)], + }, + ) + ) for line in self.employee_timesheet_ids: price = price_list.get_product_price( product=line.product_id, quantity=line.unit_amount, - partner=invoice.partner_id, + partner=invoice_vals.get("partner_id"), date=False, uom_id=False, ) template = line.product_id.product_tmpl_id accounts = template.get_product_accounts() account = accounts["income"] - vals = { - "product_id": line.product_id.id, - "account_analytic_id": line.account_id.id, - "quantity": line.unit_amount, - "name": line.name, - "price_unit": price, - "account_id": account.id, - "invoice_id": invoice.id, - "fsm_order_id": self.id, - } - time_cost = self.env["account.invoice.line"].create(vals) taxes = template.taxes_id - time_cost.invoice_line_tax_ids = fpos.map_tax(taxes) - invoice.compute_taxes() + tax_ids = fpos.map_tax(taxes) + invoice_line_vals.append( + ( + 0, + 0, + { + "product_id": line.product_id.id, + "analytic_account_id": line.account_id.id, + "quantity": line.unit_amount, + "name": line.name, + "price_unit": price, + "account_id": account.id, + "fsm_order_ids": [(4, self.id)], + "tax_ids": [(6, 0, tax_ids.ids)], + }, + ) + ) + + invoice_vals.update({"invoice_line_ids": invoice_line_vals}) + invoice = self.env["account.move"].sudo().create(invoice_vals) + + invoice._recompute_tax_lines() self.account_stage = "invoiced" return invoice diff --git a/fieldservice_isp_account/models/fsm_order_cost.py b/fieldservice_isp_account/models/fsm_order_cost.py new file mode 100644 index 0000000000..1eff39731e --- /dev/null +++ b/fieldservice_isp_account/models/fsm_order_cost.py @@ -0,0 +1,24 @@ +# Copyright 2021 ACSONE SA/NV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models + + +class FsmOrderCost(models.Model): + + _name = "fsm.order.cost" + _description = "Fsm Order Cost" + + fsm_order_id = fields.Many2one(comodel_name="fsm.order", required=True,) + price_unit = fields.Float(string="Unit Price", required=True,) + quantity = fields.Float(string="Quantity", required=True, default=1,) + product_id = fields.Many2one( + comodel_name="product.product", string="Product", required=True, + ) + + @api.onchange("product_id") + def onchange_product_id(self): + for cost in self: + if not cost.product_id: + continue + cost.price_unit = cost.product_id.standard_price diff --git a/fieldservice_isp_account/models/fsm_person.py b/fieldservice_isp_account/models/fsm_person.py index 49ea2a9cc7..d88787a80a 100644 --- a/fieldservice_isp_account/models/fsm_person.py +++ b/fieldservice_isp_account/models/fsm_person.py @@ -1,7 +1,7 @@ # Copyright (C) 2018 - TODAY, Open Source Integrators # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import api, fields, models +from odoo import fields, models class FSMPerson(models.Model): @@ -10,19 +10,18 @@ class FSMPerson(models.Model): bill_count = fields.Integer(string="Vendor Bills", compute="_compute_vendor_bills") def _compute_vendor_bills(self): - self.bill_count = self.env["account.invoice"].search_count( + self.bill_count = self.env["account.move"].search_count( [("partner_id", "=", self.partner_id.id)] ) - @api.multi def action_view_bills(self): for bill in self: action = self.env.ref("account.action_invoice_tree1").read()[0] - vendor_bills = self.env["account.invoice"].search( + vendor_bills = self.env["account.move"].search( [("partner_id", "=", bill.partner_id.id)] ) if len(vendor_bills) == 1: - action["views"] = [(self.env.ref("account.invoice_form").id, "form")] + action["views"] = [(self.env.ref("account.view_move_form").id, "form")] action["res_id"] = vendor_bills.id else: action["domain"] = [("id", "in", vendor_bills.ids)] diff --git a/fieldservice_isp_account/security/fsm_order_cost.xml b/fieldservice_isp_account/security/fsm_order_cost.xml new file mode 100644 index 0000000000..e8d0ef7fcb --- /dev/null +++ b/fieldservice_isp_account/security/fsm_order_cost.xml @@ -0,0 +1,23 @@ + + + + + fsm.order.cost access base user + + + + + + + + + fsm.order.cost access fieldservice user + + + + + + + + diff --git a/fieldservice_isp_account/static/description/index.html b/fieldservice_isp_account/static/description/index.html index 04cc1226d9..1dab785e80 100644 --- a/fieldservice_isp_account/static/description/index.html +++ b/fieldservice_isp_account/static/description/index.html @@ -367,7 +367,7 @@

Field Service - ISP Accounting

!! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! --> -

Beta License: AGPL-3 OCA/field-service Translate me on Weblate Try me on Runbot

+

Beta License: AGPL-3 OCA/field-service Translate me on Weblate Try me on Runbot

This module adds the ability to track employee time and contractor costs for Field Service Orders. It also adds functionality to create a customer invoice and a vendor bill when a Field Service Order is @@ -413,7 +413,7 @@

Bug Tracker

Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us smashing it by providing a detailed and welcomed -feedback.

+feedback.

Do not contact contributors directly about support or help with technical issues.

@@ -448,7 +448,7 @@

Maintainers

promote its widespread use.

Current maintainers:

osimallen brian10048 bodedra

-

This module is part of the OCA/field-service project on GitHub.

+

This module is part of the OCA/field-service project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

diff --git a/fieldservice_isp_account/tests/test_isp_account.py b/fieldservice_isp_account/tests/test_isp_account.py index 8347ca24a8..cf600c26ac 100644 --- a/fieldservice_isp_account/tests/test_isp_account.py +++ b/fieldservice_isp_account/tests/test_isp_account.py @@ -3,27 +3,42 @@ from odoo import fields from odoo.exceptions import ValidationError +from odoo.tests import SavepointCase -from odoo.addons.fieldservice_account.tests.test_fsm_account import FSMAccountCase - -class FSMAccountCase(FSMAccountCase): - def setUp(self): - super(FSMAccountCase, self).setUp() - self.test_person = self.env["fsm.person"].create({"name": "Worker-1"}) - self.test_analytic = self.env.ref("analytic.analytic_administratif") - self.account_id = self.env["account.account"].create( +class FSMISPAccountCase(SavepointCase): + @classmethod + def setUpClass(cls): + super(FSMISPAccountCase, cls).setUpClass() + cls.AccountMoveLine = cls.env["account.move.line"] + cls.test_person = cls.env["fsm.person"].create({"name": "Worker-1"}) + cls.test_analytic = cls.env.ref("analytic.analytic_administratif") + cls.account_id = cls.env["account.account"].create( { "code": "NC1110", "name": "Test Payable Account", - "user_type_id": self.env.ref("account.data_account_type_payable").id, + "user_type_id": cls.env.ref("account.data_account_type_payable").id, "reconcile": True, } ) + # create a Res Partner to be converted to FSM Location/Person + cls.test_loc_partner = cls.env["res.partner"].create( + {"name": "Test Loc Partner", "phone": "ABC", "email": "tlp@email.com"} + ) + # create expected FSM Location to compare to converted FSM Location + cls.test_location = cls.env["fsm.location"].create( + { + "name": "Test Location", + "phone": "123", + "email": "tp@email.com", + "partner_id": cls.test_loc_partner.id, + "owner_id": cls.test_loc_partner.id, + "customer_id": cls.test_loc_partner.id, + } + ) def _create_workorder(self, bill_to, contractors, timesheets): # Create a new work order - contractors = self.env["account.invoice.line"].create(contractors) timesheets = self.env["account.analytic.line"].create(timesheets) order = self.env["fsm.order"].create( @@ -31,10 +46,13 @@ def _create_workorder(self, bill_to, contractors, timesheets): "location_id": self.test_location.id, "bill_to": bill_to, "person_id": self.test_person.id, - "contractor_cost_ids": [(6, 0, contractors.ids)], "employee_timesheet_ids": [(6, 0, timesheets.ids)], } ) + for contractor in contractors: + contractor.update({"fsm_order_id": order.id}) + contractors = self.env["fsm.order.cost"].create(contractors) + order.write({"contractor_cost_ids": [(6, 0, contractors.ids)]}) order.person_ids += self.test_person return order @@ -47,18 +65,18 @@ def _process_order_to_invoices(self, order): self.assertEqual(order.account_stage, "review") # Create vendor bill # Vendor bill created from order's contractor - if not order.person_id.partner_id.supplier: + if not order.person_id.partner_id.supplier_rank: with self.assertRaises(ValidationError) as e: order.account_confirm() self.assertEqual( e.exception.name, "The worker assigned to this order is not a supplier" ) - order.person_id.partner_id.supplier = True + order.person_id.partner_id.supplier_rank = True order.account_confirm() self.assertEqual(order.account_stage, "confirmed") - bill = self.AccountInvoice.search( - [("type", "=", "in_invoice"), ("fsm_order_id", "=", order.id)] - ) + bill = self.AccountMoveLine.search( + [("fsm_order_ids", "in", order.id)] + ).move_id.filtered(lambda i: i.type == "in_invoice") self.assertEqual(len(bill), 1) self.assertEqual(len(order.contractor_cost_ids), len(bill.invoice_line_ids)) # Customer invoice created from order's contractor and timehsheet @@ -68,9 +86,9 @@ def _process_order_to_invoices(self, order): order.customer_id = self.test_loc_partner # Assign some partner order.account_create_invoice() self.assertEqual(order.account_stage, "invoiced") - invoice = self.AccountInvoice.search( - [("type", "=", "out_invoice"), ("fsm_order_id", "=", order.id)] - ) + invoice = self.AccountMoveLine.search( + [("fsm_order_ids", "in", order.id)] + ).move_id.filtered(lambda i: i.type == "out_invoice") self.assertEqual(len(invoice), 1) self.assertEqual( len(order.contractor_cost_ids) + len(order.employee_timesheet_ids), @@ -113,9 +131,7 @@ def test_fsm_order_bill_to_location(self): self.test_location.analytic_account_id = self.test_analytic contractors = [ { - "name": "contractor_line_1", "product_id": self.env.ref("product.expense_hotel").id, - "account_id": self.account_id.id, "quantity": 2, "price_unit": 200, }, @@ -146,10 +162,9 @@ def test_fsm_order_bill_to_location(self): self.assertEqual(order.contractor_total, 400) self.assertEqual(order.employee_time_total, 10) # Hrs self.assertEqual(order.total_cost, 1400) - # Testing not working "Need to Configure Chart of Accounts" - # bill, invoice = self._process_order_to_invoices(order) - # self.assertEqual(bill.partner_id, order.person_id.partner_id) - # self.assertEqual(invoice.partner_id, order.location_id.customer_id) + bill, invoice = self._process_order_to_invoices(order) + self.assertEqual(bill.partner_id, order.person_id.partner_id) + self.assertEqual(invoice.partner_id, order.location_id.customer_id) def test_fsm_order_bill_to_contact(self): """Bill To Contact, @@ -160,16 +175,12 @@ def test_fsm_order_bill_to_contact(self): # Create a new work order with contract = 500 and timesheet = 300 contractors = [ { - "name": "contractor_line_2", "product_id": self.env.ref("product.expense_hotel").id, - "account_id": self.account_id.id, "quantity": 2, "price_unit": 100, }, { - "name": "contractor_line_3", "product_id": self.env.ref("product.expense_hotel").id, - "account_id": self.account_id.id, "quantity": 1, "price_unit": 300, }, diff --git a/fieldservice_isp_account/views/account.xml b/fieldservice_isp_account/views/account.xml index 20ab239899..d9fd69d762 100644 --- a/fieldservice_isp_account/views/account.xml +++ b/fieldservice_isp_account/views/account.xml @@ -3,7 +3,6 @@ Service Orders fsm.order - form tree,form diff --git a/fieldservice_isp_account/views/fsm_order.xml b/fieldservice_isp_account/views/fsm_order.xml index 2563e4e60c..1625294076 100644 --- a/fieldservice_isp_account/views/fsm_order.xml +++ b/fieldservice_isp_account/views/fsm_order.xml @@ -42,20 +42,9 @@ - - - - - - - + + + @@ -90,6 +79,11 @@ /> + From 3827600c2c5b5aba02b178689917504c84dedbd2 Mon Sep 17 00:00:00 2001 From: Benoit Aimont Date: Mon, 7 Jun 2021 16:47:05 +0200 Subject: [PATCH 30/33] [IMP] a field name customer_id in fieldservice_account_analytic should have a string Customer --- fieldservice_isp_account/models/fsm_order.py | 4 ++-- fieldservice_isp_account/tests/test_isp_account.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/fieldservice_isp_account/models/fsm_order.py b/fieldservice_isp_account/models/fsm_order.py index 68762b024e..0e954dd9f9 100644 --- a/fieldservice_isp_account/models/fsm_order.py +++ b/fieldservice_isp_account/models/fsm_order.py @@ -151,9 +151,9 @@ def account_create_invoice(self): ], limit=1, ) - if self.bill_to == "contact": + if self.bill_to == "customer": if not self.customer_id: - raise ValidationError(_("Contact empty")) + raise ValidationError(_("Customer empty")) fpos = self.customer_id.property_account_position_id invoice_vals = { "partner_id": self.customer_id.id, diff --git a/fieldservice_isp_account/tests/test_isp_account.py b/fieldservice_isp_account/tests/test_isp_account.py index cf600c26ac..4e5b7ec76e 100644 --- a/fieldservice_isp_account/tests/test_isp_account.py +++ b/fieldservice_isp_account/tests/test_isp_account.py @@ -80,7 +80,7 @@ def _process_order_to_invoices(self, order): self.assertEqual(len(bill), 1) self.assertEqual(len(order.contractor_cost_ids), len(bill.invoice_line_ids)) # Customer invoice created from order's contractor and timehsheet - if order.bill_to == "contact" and not order.customer_id: + if order.bill_to == "customer" and not order.customer_id: with self.assertRaises(ValidationError): order.account_create_invoice() order.customer_id = self.test_loc_partner # Assign some partner @@ -196,7 +196,7 @@ def test_fsm_order_bill_to_contact(self): }, ] order = self._create_workorder( - bill_to="contact", contractors=contractors, timesheets=timesheets + bill_to="customer", contractors=contractors, timesheets=timesheets ) order._compute_contractor_cost() order._compute_employee_hours() From 0781fa38f4bd200aadcb02299b3da27c86cc539f Mon Sep 17 00:00:00 2001 From: Benoit Aimont Date: Thu, 10 Jun 2021 08:48:30 +0200 Subject: [PATCH 31/33] [ADD] make invoice_vals inheritable --- fieldservice_isp_account/models/fsm_order.py | 219 ++++++++++--------- 1 file changed, 121 insertions(+), 98 deletions(-) diff --git a/fieldservice_isp_account/models/fsm_order.py b/fieldservice_isp_account/models/fsm_order.py index 0e954dd9f9..b968af9639 100644 --- a/fieldservice_isp_account/models/fsm_order.py +++ b/fieldservice_isp_account/models/fsm_order.py @@ -95,27 +95,7 @@ def create_bills(self): fpos = self.person_id.partner_id.property_account_position_id invoice_line_vals = [] for cost in self.contractor_cost_ids: - template = cost.product_id.product_tmpl_id - accounts = template.get_product_accounts() - account = accounts["expense"] - taxes = template.supplier_taxes_id - tax_ids = fpos.map_tax(taxes) - invoice_line_vals.append( - ( - 0, - 0, - { - "analytic_account_id": self.location_id.analytic_account_id.id, - "product_id": cost.product_id.id, - "quantity": cost.quantity, - "name": cost.product_id.display_name, - "price_unit": cost.price_unit, - "account_id": account.id, - "fsm_order_ids": [(4, self.id)], - "tax_ids": [(6, 0, tax_ids.ids)], - }, - ) - ) + invoice_line_vals.append((0, 0, self._get_bill_line_vals(cost, fpos))) vals = { "partner_id": self.person_id.partner_id.id, "type": "in_invoice", @@ -128,6 +108,23 @@ def create_bills(self): bill = self.env["account.move"].sudo().create(vals) bill._recompute_tax_lines() + def _get_bill_line_vals(self, cost, fpos): + template = cost.product_id.product_tmpl_id + accounts = template.get_product_accounts() + account = accounts["expense"] + taxes = template.supplier_taxes_id + tax_ids = fpos.map_tax(taxes) + return { + "analytic_account_id": self.location_id.analytic_account_id.id, + "product_id": cost.product_id.id, + "quantity": cost.quantity, + "name": cost.product_id.display_name, + "price_unit": cost.price_unit, + "account_id": account.id, + "fsm_order_ids": [(4, self.id)], + "tax_ids": [(6, 0, tax_ids.ids)], + } + def account_confirm(self): for order in self: contractor = order.person_id.partner_id.supplier_rank @@ -143,96 +140,28 @@ def account_confirm(self): order.account_stage = "confirmed" def account_create_invoice(self): - jrnl = self.env["account.journal"].search( - [ - ("company_id", "=", self.env.company.id), - ("type", "=", "sale"), - ("active", "=", True), - ], - limit=1, - ) - if self.bill_to == "customer": - if not self.customer_id: - raise ValidationError(_("Customer empty")) - fpos = self.customer_id.property_account_position_id - invoice_vals = { - "partner_id": self.customer_id.id, - "type": "out_invoice", - "journal_id": jrnl.id or False, - "fiscal_position_id": fpos.id or False, - "fsm_order_ids": [(4, self.id)], - } - price_list = self.customer_id.property_product_pricelist - - else: - fpos = self.location_id.customer_id.property_account_position_id - invoice_vals = { - "partner_id": self.location_id.customer_id.id, - "type": "out_invoice", - "journal_id": jrnl.id or False, - "fiscal_position_id": fpos.id or False, - "fsm_order_ids": [(4, self.id)], - "company_id": self.env.company.id, - } - price_list = self.location_id.customer_id.property_product_pricelist - + fpos = self._get_fpos() + invoice_vals = self._get_invoice_vals(fpos) + price_list = self._get_pricelist() invoice_line_vals = [] for cost in self.contractor_cost_ids: - price = price_list.get_product_price( - product=cost.product_id, - quantity=cost.quantity, - partner=invoice_vals.get("partner_id"), - date=False, - uom_id=False, - ) - template = cost.product_id.product_tmpl_id - accounts = template.get_product_accounts() - account = accounts["income"] - taxes = template.taxes_id - tax_ids = fpos.map_tax(taxes) invoice_line_vals.append( ( 0, 0, - { - "product_id": cost.product_id.id, - "analytic_account_id": self.location_id.analytic_account_id.id, - "quantity": cost.quantity, - "name": cost.product_id.display_name, - "price_unit": price, - "account_id": account.id, - "fsm_order_ids": [(4, self.id)], - "tax_ids": [(6, 0, tax_ids.ids)], - }, + self._get_cost_invoice_line_vals( + cost, fpos, price_list, invoice_vals + ), ) ) for line in self.employee_timesheet_ids: - price = price_list.get_product_price( - product=line.product_id, - quantity=line.unit_amount, - partner=invoice_vals.get("partner_id"), - date=False, - uom_id=False, - ) - template = line.product_id.product_tmpl_id - accounts = template.get_product_accounts() - account = accounts["income"] - taxes = template.taxes_id - tax_ids = fpos.map_tax(taxes) invoice_line_vals.append( ( 0, 0, - { - "product_id": line.product_id.id, - "analytic_account_id": line.account_id.id, - "quantity": line.unit_amount, - "name": line.name, - "price_unit": price, - "account_id": account.id, - "fsm_order_ids": [(4, self.id)], - "tax_ids": [(6, 0, tax_ids.ids)], - }, + self._get_timesheet_line_invoice_line_vals( + line, fpos, price_list, invoice_vals + ), ) ) @@ -243,5 +172,99 @@ def account_create_invoice(self): self.account_stage = "invoiced" return invoice + def _get_pricelist(self): + if self.bill_to == "customer": + if not self.customer_id: + raise ValidationError(_("Customer empty")) + return self.customer_id.property_product_pricelist + elif self.bill_to == "location": + return self.location_id.customer_id.property_product_pricelist + + def _get_fpos(self): + if self.bill_to == "customer": + if not self.customer_id: + raise ValidationError(_("Customer empty")) + return self.customer_id.property_account_position_id + elif self.bill_to == "location": + return self.location_id.customer_id.property_account_position_id + + def _get_invoice_vals(self, fpos): + jrnl = self.env["account.journal"].search( + [ + ("company_id", "=", self.env.company.id), + ("type", "=", "sale"), + ("active", "=", True), + ], + limit=1, + ) + invoice_vals = { + "journal_id": jrnl.id or False, + "type": "out_invoice", + "fiscal_position_id": fpos.id or False, + "fsm_order_ids": [(4, self.id)], + } + if self.bill_to == "customer": + if not self.customer_id: + raise ValidationError(_("Customer empty")) + invoice_vals.update({ + "partner_id": self.customer_id.id, + }) + elif self.bill_to == "location": + invoice_vals.update({ + "partner_id": self.location_id.customer_id.id, + "company_id": self.env.company.id, + }) + return invoice_vals + + def _get_cost_invoice_line_vals(self, cost, fpos, price_list, invoice_vals): + price = price_list.get_product_price( + product=cost.product_id, + quantity=cost.quantity, + partner=invoice_vals.get("partner_id"), + date=False, + uom_id=False, + ) + template = cost.product_id.product_tmpl_id + accounts = template.get_product_accounts() + account = accounts["income"] + taxes = template.taxes_id + tax_ids = fpos.map_tax(taxes) + return { + "product_id": cost.product_id.id, + "analytic_account_id": self.location_id.analytic_account_id.id, + "quantity": cost.quantity, + "name": cost.product_id.display_name, + "price_unit": price, + "account_id": account.id, + "fsm_order_ids": [(4, self.id)], + "tax_ids": [(6, 0, tax_ids.ids)], + } + + def _get_timesheet_line_invoice_line_vals( + self, line, fpos, price_list, invoice_vals + ): + price = price_list.get_product_price( + product=line.product_id, + quantity=line.unit_amount, + partner=invoice_vals.get("partner_id"), + date=False, + uom_id=False, + ) + template = line.product_id.product_tmpl_id + accounts = template.get_product_accounts() + account = accounts["income"] + taxes = template.taxes_id + tax_ids = fpos.map_tax(taxes) + return { + "product_id": line.product_id.id, + "analytic_account_id": line.account_id.id, + "quantity": line.unit_amount, + "name": line.name, + "price_unit": price, + "account_id": account.id, + "fsm_order_ids": [(4, self.id)], + "tax_ids": [(6, 0, tax_ids.ids)], + } + def account_no_invoice(self): self.account_stage = "no" From 366a044de612ecc627b607a9eb5dc6abd55a957a Mon Sep 17 00:00:00 2001 From: Benoit Aimont Date: Fri, 9 Jul 2021 15:56:59 +0200 Subject: [PATCH 32/33] [ADD] make invoice_line_vals inheritable --- fieldservice_isp_account/models/fsm_order.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/fieldservice_isp_account/models/fsm_order.py b/fieldservice_isp_account/models/fsm_order.py index b968af9639..a4a4123774 100644 --- a/fieldservice_isp_account/models/fsm_order.py +++ b/fieldservice_isp_account/models/fsm_order.py @@ -139,10 +139,7 @@ def account_confirm(self): if order.employee_timesheet_ids: order.account_stage = "confirmed" - def account_create_invoice(self): - fpos = self._get_fpos() - invoice_vals = self._get_invoice_vals(fpos) - price_list = self._get_pricelist() + def _get_invoice_line_vals(self, fpos, price_list, invoice_vals): invoice_line_vals = [] for cost in self.contractor_cost_ids: invoice_line_vals.append( @@ -164,10 +161,15 @@ def account_create_invoice(self): ), ) ) + return invoice_line_vals + def account_create_invoice(self): + fpos = self._get_fpos() + invoice_vals = self._get_invoice_vals(fpos) + price_list = self._get_pricelist() + invoice_line_vals = self._get_invoice_line_vals(fpos, price_list, invoice_vals) invoice_vals.update({"invoice_line_ids": invoice_line_vals}) invoice = self.env["account.move"].sudo().create(invoice_vals) - invoice._recompute_tax_lines() self.account_stage = "invoiced" return invoice From 76318f1523583025c4ac1b34243b0869cab4f50a Mon Sep 17 00:00:00 2001 From: Benoit Aimont Date: Tue, 23 Nov 2021 22:35:50 +0100 Subject: [PATCH 33/33] [ADD] add product uom to invoice line vals --- fieldservice_isp_account/models/fsm_order.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fieldservice_isp_account/models/fsm_order.py b/fieldservice_isp_account/models/fsm_order.py index a4a4123774..b35c8c022a 100644 --- a/fieldservice_isp_account/models/fsm_order.py +++ b/fieldservice_isp_account/models/fsm_order.py @@ -117,6 +117,7 @@ def _get_bill_line_vals(self, cost, fpos): return { "analytic_account_id": self.location_id.analytic_account_id.id, "product_id": cost.product_id.id, + "product_uom_id": cost.product_id.uom_id.id, "quantity": cost.quantity, "name": cost.product_id.display_name, "price_unit": cost.price_unit, @@ -233,6 +234,7 @@ def _get_cost_invoice_line_vals(self, cost, fpos, price_list, invoice_vals): tax_ids = fpos.map_tax(taxes) return { "product_id": cost.product_id.id, + "product_uom_id": cost.product_id.uom_id.id, "analytic_account_id": self.location_id.analytic_account_id.id, "quantity": cost.quantity, "name": cost.product_id.display_name, @@ -259,6 +261,7 @@ def _get_timesheet_line_invoice_line_vals( tax_ids = fpos.map_tax(taxes) return { "product_id": line.product_id.id, + "product_uom_id": line.product_id.uom_id.id, "analytic_account_id": line.account_id.id, "quantity": line.unit_amount, "name": line.name,