File: purchase.py

package info (click to toggle)
tryton-modules-product-kit 7.0.4-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 580 kB
  • sloc: python: 1,450; xml: 124; makefile: 11; sh: 3
file content (188 lines) | stat: -rw-r--r-- 6,529 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
# This file is part of Tryton.  The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.

from decimal import Decimal

from trytond.i18n import gettext
from trytond.model import ModelSQL, ModelView, fields
from trytond.modules.product import round_price
from trytond.pool import Pool, PoolMeta

from .common import (
    AmendmentLineMixin, get_moves, get_shipments_returns,
    handle_shipment_exception_mixin, order_line_component_mixin,
    order_line_mixin, order_mixin, search_moves, search_shipments_returns)
from .product import ComponentMixin


class Purchase(order_mixin('purchase'), metaclass=PoolMeta):
    __name__ = 'purchase.purchase'

    def create_move(self, move_type):
        moves = super().create_move(move_type)
        for line in self.lines:
            if line.components:
                for component in line.components:
                    move = component.get_move(move_type)
                    if move:
                        moves.append(move)
        return moves

    @get_shipments_returns('stock.shipment.in')
    def get_shipments(self, name):
        return super().get_shipments(name)

    @get_shipments_returns('stock.shipment.in.return')
    def get_shipment_returns(self, name):
        return super().get_shipment_returns(name)

    @classmethod
    @search_shipments_returns('stock.shipment.in')
    def search_shipments(cls, name, clause):
        return super().search_shipments(name, clause)

    @classmethod
    @search_shipments_returns('stock.shipment.in.return')
    def search_shipment_returns(cls, name, clause):
        return super().search_shipment_returns(name, clause)

    @get_moves
    def get_moves(self, name):
        return super().get_moves(name)

    @classmethod
    @search_moves
    def search_moves(cls, name, clause):
        return super().search_moves(name, clause)


class Line(order_line_mixin('purchase'), metaclass=PoolMeta):
    __name__ = 'purchase.line'

    def get_component_order_line(self, component, **values):
        values = values.copy()
        values['purchase'] = self.purchase
        line = super().get_component_order_line(component, **values)
        if line.unit_price is None:
            line.unit_price = round_price(Decimal(0))
        return line

    @classmethod
    def get_move_product_types(cls):
        types = super().get_move_product_types()
        types.append('kit')
        return types


class LineComponent(
        order_line_component_mixin('purchase'), ComponentMixin,
        ModelSQL, ModelView):
    "Purchase Line Component"
    __name__ = 'purchase.line.component'

    def get_move(self, move_type):
        from trytond.modules.purchase.exceptions import PartyLocationError
        pool = Pool()
        Move = pool.get('stock.move')

        if (self.quantity >= 0) != (move_type == 'in'):
            return

        quantity = (
            self._get_move_quantity(move_type)
            - self._get_shipped_quantity(move_type))

        quantity = self.unit.round(quantity)
        if quantity <= 0:
            return

        if not self.line.purchase.party.supplier_location:
            raise PartyLocationError(
                gettext('purchase.msg_purchase_supplier_location_required',
                    purchase=self.purchase.rec_name,
                    party=self.purchase.party.rec_name))

        move = Move()
        move.quantity = quantity
        move.unit = self.unit
        move.product = self.product
        move.from_location = self.line.from_location
        move.to_location = self.line.to_location
        move.state = 'draft'
        move.company = self.line.purchase.company
        if move.on_change_with_unit_price_required():
            move.unit_price = round_price(
                self.line.unit_price * self.price_ratio)
            move.currency = self.line.purchase.currency
        move.planned_date = self.line.planned_delivery_date
        move.origin = self
        move.origin_planned_date = move.planned_date
        return move

    def _get_move_quantity(self, shipment_type):
        'Return the quantity that should be shipped'
        return abs(self.quantity)

    def check_move_quantity(self):
        from trytond.modules.purchase.exceptions import PurchaseMoveQuantity
        pool = Pool()
        Lang = pool.get('ir.lang')
        Warning = pool.get('res.user.warning')
        lang = Lang.get()
        move_type = 'in' if self.quantity >= 0 else 'return'
        quantity = (
            self._get_move_quantity(move_type)
            - self._get_shipped_quantity(move_type))
        if quantity < 0:
            warning_name = Warning.format(
                'check_move_quantity', [self])
            if Warning.check(warning_name):
                raise PurchaseMoveQuantity(warning_name, gettext(
                        'purchase.msg_purchase_line_move_quantity',
                        line=self.rec_name,
                        extra=lang.format_number_symbol(
                            -quantity, self.unit),
                        quantity=lang.format_number_symbol(
                            self.quantity, self.unit)))


class LineComponentIgnoredMove(ModelSQL):
    'Purchase Line Component - Ignored Move'
    __name__ = 'purchase.line.component-ignored-stock.move'
    component = fields.Many2One(
        'purchase.line.component', "Component",
        ondelete='CASCADE', required=True)
    move = fields.Many2One(
        'stock.move', "Move", ondelete='RESTRICT', required=True)


class LineComponentRecreatedMove(ModelSQL):
    'Purchase Line Component - Recreated Move'
    __name__ = 'purchase.line.component-recreated-stock.move'
    component = fields.Many2One(
        'purchase.line.component', "Component",
        ondelete='CASCADE', required=True)
    move = fields.Many2One(
        'stock.move', "Move", ondelete='RESTRICT', required=True)


class HandleShipmentException(
        handle_shipment_exception_mixin('purchase'),
        metaclass=PoolMeta):
    __name__ = 'purchase.handle.shipment.exception'


class Amendment(metaclass=PoolMeta):
    __name__ = 'purchase.amendment'

    @classmethod
    def _stock_moves(cls, line):
        yield from super()._stock_moves(line)
        for component in line.components:
            for move in component.moves:
                if move.state == 'draft':
                    yield move


class AmendmentLine(AmendmentLineMixin, metaclass=PoolMeta):
    __name__ = 'purchase.amendment.line'