• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OCA / account-closing / 348

pending completion
348

push

travis-ci

web-flow
Merge pull request #39 from jcoux/migrate_cuttoff_accrual_picking_from_launchpad

[WIP] Migrate cutoff accrual picking from launchpad

201 of 201 new or added lines in 6 files covered. (100.0%)

565 of 847 relevant lines covered (66.71%)

1.33 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

10.71
/account_cutoff_accrual_picking/models/account_cutoff.py
1
# -*- coding: utf-8 -*-
2
# © 2013-2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
3
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
4

5
from openerp import models, fields, api, _
2✔
6
from openerp.tools import float_is_zero
2✔
7
from openerp.exceptions import Warning as UserError
2✔
8

9

10
class AccountCutoff(models.Model):
2✔
11
    _inherit = 'account.cutoff'
2✔
12

13
    @api.model
2✔
14
    def _prepare_lines_from_move_lines(
15
            self, cutoff, stock_move, account_mapping):
16
        ato = self.env['account.tax']
×
17
        dpo = self.env['decimal.precision']
×
18
        assert cutoff.type in ('accrued_expense', 'accrued_revenue'),\
×
19
            "The field 'type' has a wrong value"
20
        qty_prec = dpo.precision_get('Product Unit of Measure')
×
21
        acc_prec = dpo.precision_get('Account')
×
22
        qty = stock_move.product_qty
×
23
        product = stock_move.product_id
×
24
        if float_is_zero(qty, precision_digits=qty_prec):
×
25
            return False
×
26
        if stock_move.invoice_line_ids:
×
27
            # In real life, all move lines related to an 1 invoice line
28
            # should be in the same state and have the same date
29
            inv_line = stock_move.invoice_line_ids[0]
×
30
            inv = inv_line.invoice_id
×
31
            if (
×
32
                    inv.state not in ('open', 'paid') and
33
                    inv.date_invoice <= cutoff.cutoff_date):
34
                return False
×
35
            account = inv_line.account_id
×
36
            currency = inv.currency_id
×
37
            analytic_account_id = inv_line.account_analytic_id.id or False
×
38
            price_unit = inv_line.price_unit * \
×
39
                (1 - (inv_line.discount or 0.0) / 100.0)
40
            uom = inv_line.uos_id
×
41
            taxes = inv_line.invoice_line_tax_id
×
42
            partner = inv.commercial_partner_id
×
43
            price_source = 'invoice'
×
44
        elif cutoff.type == 'accrued_expense':
×
45
            pur_line = stock_move.purchase_line_id
×
46
            if not pur_line:
×
47
                return False
×
48
            purchase = pur_line.order_id
×
49
            account = product.property_account_expense
×
50
            if not account:
×
51
                account = product.categ_id.property_account_expense_categ
×
52
            if not account:
×
53
                raise UserError(_(
×
54
                    "Missing expense account on product '%s' or on its "
55
                    "related product category '%s' (Picking '%s').") % (
56
                        product.name_get()[0][1],
57
                        product.categ_id.complete_name,
58
                        stock_move.picking_id.name))
59
            account = purchase.fiscal_position.map_account(account)
×
60
            currency = purchase.currency_id
×
61
            analytic_account_id = pur_line.account_analytic_id.id or False
×
62
            price_unit = pur_line.price_unit
×
63
            uom = pur_line.product_uom
×
64
            taxes = pur_line.taxes_id
×
65
            partner = purchase.partner_id.commercial_partner_id
×
66
            price_source = 'purchase'
×
67
        elif cutoff.type == 'accrued_revenue':
×
68
            so_line = stock_move.procurement_id.sale_line_id
×
69
            if not so_line:
×
70
                return False
×
71
            so = so_line.order_id
×
72
            account = product.property_account_income
×
73
            if not account:
×
74
                account = product.categ_id.property_account_income_categ
×
75
            if not account:
×
76
                raise UserError(_(
×
77
                    "Missing income account on product '%s' or on its "
78
                    "related product category '%s' (Picking '%s').") % (
79
                        product.name_get()[0][1],
80
                        product.categ_id.complete_name,
81
                        stock_move.picking_id.name))
82
            account = so.fiscal_position.map_account(account)
×
83
            currency = so.currency_id
×
84
            analytic_account_id = so.project_id.id or False
×
85
            price_unit = so_line.price_unit *\
×
86
                (1 - (so_line.discount or 0.0) / 100.0)
87
            uom = so_line.product_uom
×
88
            taxes = so_line.tax_id
×
89
            partner = so.partner_id.commercial_partner_id
×
90
            price_source = 'sale'
×
91

92
        company_currency = cutoff.company_currency_id
×
93
        # update price_unit to be in product uom
94
        price_unit = self.env['product.uom']._compute_qty_obj(
×
95
            uom, price_unit, stock_move.product_id.uom_id)
96
        sign = cutoff.type == 'accrued_expense' and -1 or 1
×
97
        tax_line_ids = []
×
98
        tax_res = taxes.compute_all(
×
99
            price_unit, qty, product=stock_move.product_id, partner=partner)
100
        amount = tax_res['total'] * sign  # =total without taxes
×
101
        price_unit_without_tax = amount / qty
×
102
        for tax_line in tax_res['taxes']:
×
103
            tax = ato.browse(tax_line['id'])
×
104
            if float_is_zero(tax_line['amount'], precision_digits=acc_prec):
×
105
                continue
×
106
            if cutoff.type == 'accrued_expense':
×
107
                tax_accrual_account_id = tax.account_accrued_expense_id.id
×
108
                tax_account_field_label = 'Accrued Expense Tax Account'
×
109
            elif cutoff.type == 'accrued_revenue':
×
110
                tax_accrual_account_id = tax.account_accrued_revenue_id.id
×
111
                tax_account_field_label = 'Accrued Revenue Tax Account'
×
112
            if not tax_accrual_account_id:
×
113
                raise UserError(_(
×
114
                    "Missing '%s' on tax '%s' "
115
                    "(Picking '%s', product '%s').") % (
116
                        tax_account_field_label,
117
                        tax.name,
118
                        stock_move.picking_id.name,
119
                        product.name_get()[0][1]))
120
            tax_amount = tax_line['amount'] * sign
×
121
            tax_accrual_amount = currency.with_context(
×
122
                date=cutoff.cutoff_date).compute(
123
                    tax_amount, company_currency)
124
            tax_line_ids.append((0, 0, {
×
125
                'tax_id': tax_line['id'],
126
                'base': currency.round(
127
                    tax_line['price_unit'] * qty),
128
                'amount': tax_amount,
129
                'sequence': tax_line['sequence'],
130
                'cutoff_account_id': tax_accrual_account_id,
131
                'cutoff_amount': tax_accrual_amount,
132
                'analytic_account_id':
133
                tax_line['account_analytic_collected_id'],
134
                # account_analytic_collected_id is for
135
                # invoices IN and OUT
136
                }))
137
        amount_company_currency = currency.with_context(
×
138
            date=cutoff.cutoff_date).compute(
139
                amount, company_currency)
140

141
        # we use account mapping here
142
        if account.id in account_mapping:
×
143
            accrual_account_id = account_mapping[account.id]
×
144
        else:
145
            accrual_account_id = account.id
×
146
        res = {
×
147
            'parent_id': cutoff.id,
148
            'stock_move_id': stock_move.id,
149
            'partner_id': partner.id,
150
            'name': stock_move.name,
151
            'account_id': account.id,
152
            'cutoff_account_id': accrual_account_id,
153
            'analytic_account_id': analytic_account_id,
154
            'currency_id': currency.id,
155
            'quantity': qty,
156
            'price_unit': price_unit_without_tax,
157
            'tax_ids': [(6, 0, taxes.ids)],
158
            'amount': amount,
159
            'cutoff_amount': amount_company_currency,
160
            'tax_line_ids': tax_line_ids,
161
            'price_source': price_source,
162
            }
163
        return res
×
164

165
    @api.multi
2✔
166
    def generate_accrual_lines(self):
167
        res = super(AccountCutoff, self).generate_accrual_lines()
×
168
        spo = self.env['stock.picking']
×
169
        aclo = self.env['account.cutoff.line']
×
170
        acmo = self.env['account.cutoff.mapping']
×
171

172
        pick_type_map = {
×
173
            'accrued_revenue': 'outgoing',
174
            'accrued_expense': 'incoming',
175
        }
176
        assert self.type in pick_type_map, \
×
177
            "self.type should be in pick_type_map.keys()"
178
        pickings = spo.search([
×
179
            ('picking_type_code', '=', pick_type_map[self.type]),
180
            ('state', '=', 'done'),
181
            ('date_done', '<=', self.cutoff_date),
182
            ('invoice_state', 'in', ('2binvoiced', 'invoiced')),
183
            '|',
184
            # when invoice_state = 2binvoiced
185
            # and invoice_state = invoiced / draft
186
            ('max_date_invoice', '=', False),
187
            ('max_date_invoice', '>', self.cutoff_date)
188
            ])
189

190
        # print "pick_ids=", pickings
191
        # Create account mapping dict
192
        account_mapping = acmo._get_mapping_dict(
×
193
            self.company_id.id, self.type)
194
        for picking in pickings:
×
195
            for move_line in picking.move_lines:
×
196
                vals = self._prepare_lines_from_move_lines(
×
197
                    self, move_line, account_mapping)
198
                if vals:
×
199
                    aclo.create(vals)
×
200
        return res
×
201

202

203
class AccountCutoffLine(models.Model):
2✔
204
    _inherit = 'account.cutoff.line'
2✔
205

206
    stock_move_id = fields.Many2one(
2✔
207
        'stock.move', string='Stock Move', readonly=True)
208
    picking_id = fields.Many2one(
2✔
209
        related='stock_move_id.picking_id', string='Picking',
210
        readonly=True, store=True)
211
    stock_move_date = fields.Datetime(
2✔
212
        related='stock_move_id.date', string='Transfer Date', readonly=True,
213
        store=True)
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc