source: rattail/rattail/db/model/batch/purchase.py @ 225a0ba

Last change on this file since 225a0ba was 225a0ba, checked in by Lance Edgar <lance@…>, 2 years ago

Add "most of" support for truck dump receiving

  • Property mode set to 100644
File size: 10.6 KB
Line 
1# -*- coding: utf-8; -*-
2################################################################################
3#
4#  Rattail -- Retail Software Framework
5#  Copyright © 2010-2018 Lance Edgar
6#
7#  This file is part of Rattail.
8#
9#  Rattail is free software: you can redistribute it and/or modify it under the
10#  terms of the GNU General Public License as published by the Free Software
11#  Foundation, either version 3 of the License, or (at your option) any later
12#  version.
13#
14#  Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
15#  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16#  FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
17#  details.
18#
19#  You should have received a copy of the GNU General Public License along with
20#  Rattail.  If not, see <http://www.gnu.org/licenses/>.
21#
22################################################################################
23"""
24Models for purchase order batches
25"""
26
27from __future__ import unicode_literals, absolute_import
28
29import six
30import sqlalchemy as sa
31from sqlalchemy import orm
32from sqlalchemy.ext.declarative import declared_attr
33
34from rattail.db.model import (Base, uuid_column, BatchMixin, BatchRowMixin,
35                              PurchaseBase, PurchaseItemBase, PurchaseCreditBase,
36                              Purchase, PurchaseItem)
37from rattail.db.model.batch import filename_column
38from rattail.util import pretty_quantity
39
40
41class PurchaseBatch(BatchMixin, PurchaseBase, Base):
42    """
43    Hopefully generic batch used for entering new purchases into the system, etc.?
44    """
45    batch_key = 'purchase'
46    __tablename__ = 'purchase_batch'
47    __batchrow_class__ = 'PurchaseBatchRow'
48    model_title = "Purchasing Batch"
49    model_title_plural = "Purchasing Batches"
50
51    @declared_attr
52    def __table_args__(cls):
53        return cls.__batch_table_args__() + cls.__purchase_table_args__() + (
54            sa.ForeignKeyConstraint(['purchase_uuid'], ['purchase.uuid'], name='purchase_batch_fk_purchase'),
55            sa.ForeignKeyConstraint(['truck_dump_batch_uuid'], ['purchase_batch.uuid'], name='purchase_batch_fk_truck_dump_batch', use_alter=True),
56        )
57
58    STATUS_OK                   = 1
59    STATUS_UNKNOWN_PRODUCT      = 2
60    STATUS_TRUCKDUMP_UNCLAIMED  = 3
61    STATUS_TRUCKDUMP_CLAIMED    = 4
62
63    STATUS = {
64        STATUS_OK                       : "ok",
65        STATUS_UNKNOWN_PRODUCT          : "has unknown product(s)",
66        STATUS_TRUCKDUMP_UNCLAIMED      : "not yet fully claimed",
67        STATUS_TRUCKDUMP_CLAIMED        : "fully claimed by child(ren)",
68    }
69
70    purchase_uuid = sa.Column(sa.String(length=32), nullable=True)
71
72    purchase = orm.relationship(
73        Purchase,
74        doc="""
75        Reference to the purchase with which the batch is associated.  May be
76        null, e.g. in the case of a "new purchase" batch.
77        """,
78        backref=orm.backref(
79            'batches',
80            order_by='PurchaseBatch.id',
81            doc="""
82            List of batches associated with the purchase.
83            """))
84
85    mode = sa.Column(sa.Integer(), nullable=False, doc="""
86    Numeric "mode" for the purchase batch, to indicate new/receiving etc.
87    """)
88
89    invoice_file = filename_column(doc="Base name for the associated invoice file, if any.")
90
91    invoice_parser_key = sa.Column(sa.String(length=100), nullable=True, doc="""
92    The key of the parser used to read the contents of the invoice file.
93    """)
94
95    truck_dump = sa.Column(sa.Boolean(), nullable=True, default=False, doc="""
96    Flag indicating whether a "receiving" batch is of the "truck dump"
97    persuasion, i.e.  it does not correspond to a single purchase order but
98    rather is assumed to represent multiple orders.
99    """)
100
101    truck_dump_batch_uuid = sa.Column(sa.String(length=32), nullable=True)
102    truck_dump_batch = orm.relationship(
103        'PurchaseBatch',
104        remote_side='PurchaseBatch.uuid',
105        doc="""
106        Reference to the "truck dump" receiving batch, for which the current
107        batch represents a single invoice which partially "consumes" the truck
108        dump.
109        """,
110        backref=orm.backref(
111            'truck_dump_children',
112            order_by='PurchaseBatch.id',
113            doc="""
114            List of batches which are "children" of the current batch, which is
115            assumed to be a truck dump.
116            """))
117
118
119class PurchaseBatchRow(BatchRowMixin, PurchaseItemBase, Base):
120    """
121    Row of data within a purchase batch.
122    """
123    __tablename__ = 'purchase_batch_row'
124    __batch_class__ = PurchaseBatch
125
126    @declared_attr
127    def __table_args__(cls):
128        return cls.__batchrow_table_args__() + cls.__purchaseitem_table_args__() + (
129            sa.ForeignKeyConstraint(['item_uuid'], ['purchase_item.uuid'], name='purchase_batch_row_fk_item'),
130        )
131
132    STATUS_OK                           = 1
133    STATUS_PRODUCT_NOT_FOUND            = 2
134    STATUS_COST_NOT_FOUND               = 3
135    STATUS_CASE_QUANTITY_UNKNOWN        = 4
136    STATUS_INCOMPLETE                   = 5
137    STATUS_ORDERED_RECEIVED_DIFFER      = 6
138    STATUS_TRUCKDUMP_UNCLAIMED          = 7
139    STATUS_TRUCKDUMP_CLAIMED            = 8
140    STATUS_TRUCKDUMP_OVERCLAIMED        = 9
141
142    STATUS = {
143        STATUS_OK                       : "ok",
144        STATUS_PRODUCT_NOT_FOUND        : "product not found",
145        STATUS_COST_NOT_FOUND           : "product found but not cost",
146        STATUS_CASE_QUANTITY_UNKNOWN    : "case quantity not known",
147        STATUS_INCOMPLETE               : "incomplete",
148        STATUS_ORDERED_RECEIVED_DIFFER  : "ordered / received differ",
149        STATUS_TRUCKDUMP_UNCLAIMED      : "not yet fully claimed",
150        STATUS_TRUCKDUMP_CLAIMED        : "fully claimed by child(ren)",
151        STATUS_TRUCKDUMP_OVERCLAIMED    : "OVER claimed by child(ren)",
152    }
153
154    item_uuid = sa.Column(sa.String(length=32), nullable=True)
155
156    item = orm.relationship(
157        PurchaseItem,
158        doc="""
159        Reference to the purchase item with which the batch row is associated.
160        May be null, e.g. in the case of a "new purchase" batch.
161        """)
162
163
164class PurchaseBatchRowClaim(Base):
165    """
166    Represents the connection between a row(s) from a truck dump batch, and the
167    corresponding "child" batch row which claims it, as well as the claimed
168    quantities etc.
169    """
170    __tablename__ = 'purchase_batch_row_claim'
171    __table_args__ = (
172        sa.ForeignKeyConstraint(['claiming_row_uuid'], ['purchase_batch_row.uuid'], name='purchase_batch_row_claim_fk_claiming_row'),
173        sa.ForeignKeyConstraint(['claimed_row_uuid'], ['purchase_batch_row.uuid'], name='purchase_batch_row_claim_fk_claimed_row'),
174    )
175
176    uuid = uuid_column()
177
178    claiming_row_uuid = sa.Column(sa.String(length=32), nullable=False)
179    claiming_row = orm.relationship(
180        PurchaseBatchRow,
181        foreign_keys='PurchaseBatchRowClaim.claiming_row_uuid',
182        doc="""
183        Reference to the "child" row which is claiming some row from a truck
184        dump batch.
185        """,
186        backref=orm.backref(
187            'truck_dump_claims',
188            cascade='all, delete-orphan',
189            doc="""
190            List of claims which this "child" row makes against rows within a
191            truck dump batch.
192            """))
193
194    claimed_row_uuid = sa.Column(sa.String(length=32), nullable=False)
195    claimed_row = orm.relationship(
196        PurchaseBatchRow,
197        foreign_keys='PurchaseBatchRowClaim.claimed_row_uuid',
198        doc="""
199        Reference to the truck dump batch row which is claimed by the "child" row.
200        """,
201        backref=orm.backref(
202            'claims',
203            # cascade='all, delete-orphan',
204            doc="""
205            List of claims made by "child" rows against this truck dump batch row.
206            """))
207
208    cases_received = sa.Column(sa.Numeric(precision=10, scale=4), nullable=True, doc="""
209    Number of cases of product which were ultimately received, and are involved in the claim.
210    """)
211
212    units_received = sa.Column(sa.Numeric(precision=10, scale=4), nullable=True, doc="""
213    Number of units of product which were ultimately received, and are involved in the claim.
214    """)
215
216    cases_damaged = sa.Column(sa.Numeric(precision=10, scale=4), nullable=True, doc="""
217    Number of cases of product which were shipped damaged, and are involved in the claim.
218    """)
219
220    units_damaged = sa.Column(sa.Numeric(precision=10, scale=4), nullable=True, doc="""
221    Number of units of product which were shipped damaged, and are involved in the claim.
222    """)
223
224    cases_expired = sa.Column(sa.Numeric(precision=10, scale=4), nullable=True, doc="""
225    Number of cases of product which were shipped expired, and are involved in the claim.
226    """)
227
228    units_expired = sa.Column(sa.Numeric(precision=10, scale=4), nullable=True, doc="""
229    Number of units of product which were shipped expired, and are involved in the claim.
230    """)
231
232
233@six.python_2_unicode_compatible
234class PurchaseBatchCredit(PurchaseCreditBase, Base):
235    """
236    Represents a working copy of purchase credit tied to a batch row.
237    """
238    __tablename__ = 'purchase_batch_credit'
239
240    @declared_attr
241    def __table_args__(cls):
242        return cls.__purchasecredit_table_args__() + (
243            sa.ForeignKeyConstraint(['row_uuid'], ['purchase_batch_row.uuid'], name='purchase_batch_credit_fk_row'),
244        )
245
246    uuid = uuid_column()
247
248    row_uuid = sa.Column(sa.String(length=32), nullable=True)
249
250    row = orm.relationship(
251        PurchaseBatchRow,
252        doc="""
253        Reference to the batch row with which the credit is associated.
254        """,
255        backref=orm.backref(
256            'credits',
257            doc="""
258            List of :class:`PurchaseBatchCredit` instances for the row.
259            """))
260
261    def __str__(self):
262        if self.cases_shorted is not None and self.units_shorted is not None:
263            qty = "{} cases, {} units".format(
264                pretty_quantity(self.cases_shorted),
265                pretty_quantity(self.units_shorted))
266        elif self.cases_shorted is not None:
267            qty = "{} cases".format(pretty_quantity(self.cases_shorted))
268        elif self.units_shorted is not None:
269            qty = "{} units".format(pretty_quantity(self.units_shorted))
270        else:
271            qty = "??"
272        qty += " {}".format(self.credit_type)
273        if self.credit_type == 'expired' and self.expiration_date:
274            qty += " ({})".format(self.expiration_date)
275        if self.credit_type == 'mispick' and self.mispick_product:
276            qty += " ({})".format(self.mispick_product)
277        if self.invoice_total:
278            return "{} = ${:0.2f}".format(qty, self.invoice_total)
279        return qty
Note: See TracBrowser for help on using the repository browser.