~homeworkprod/byceps

704d0f1642a032113f117db806fa50a477e8cb46 — Jochen Kupperschmidt 1 year, 2 months ago d20e1a2
Add total quantity field to shop article, initialize quantity from it

DDL:

    ALTER TABLE shop_articles ADD COLUMN total_quantity integer;

    -- And after updating all existing articles with a total quantity:
    ALTER TABLE shop_articles ALTER COLUMN total_quantity SET NOT NULL;
M byceps/blueprints/admin/shop/article/forms.py => byceps/blueprints/admin/shop/article/forms.py +1 -1
@@ 23,12 23,12 @@ class _ArticleBaseForm(LocalizedForm):
    description = StringField('Beschreibung')
    price = DecimalField('Stückpreis', places=2, validators=[InputRequired()])
    tax_rate = DecimalField('Steuersatz', places=3, validators=[InputRequired()])
    total_quantity = IntegerField('Gesamtmenge', validators=[InputRequired()])
    max_quantity_per_order = IntegerField('Maximale Anzahl pro Bestellung', validators=[InputRequired()])


class ArticleCreateForm(_ArticleBaseForm):
    article_number_sequence_id = SelectField('Artikelnummer-Sequenz', validators=[InputRequired()])
    quantity = IntegerField('Anzahl verfügbar', validators=[InputRequired()])

    def set_article_number_sequence_choices(self, sequences):
        sequences.sort(key=lambda seq: seq.prefix)

M byceps/blueprints/admin/shop/article/templates/admin/shop/article/create_form.html => byceps/blueprints/admin/shop/article/templates/admin/shop/article/create_form.html +1 -1
@@ 23,7 23,7 @@
      {{ form_field(form.price, size=7, maxlength=7, style='text-align: right; width: 5.5rem;', required='required', note='€') }}
      {{ form_field(form.tax_rate, list='tax_rates', size=5, maxlength=5, style='text-align: right; width: 5.5rem;', required='required', note='%') }}
      {{ form_datalist('tax_rates', ['0.00', '0.07', '0.19']) }}
      {{ form_field(form.quantity, type='number', min=0, max=99999, style='width: 5.5rem;', required='required') }}
      {{ form_field(form.total_quantity, type='number', min=0, max=99999, style='width: 5.5rem;', required='required') }}
      {{ form_field(form.max_quantity_per_order, type='number', min=1, max=99, style='width: 5.5rem;', required='required') }}
    {% endcall %}


M byceps/blueprints/admin/shop/article/templates/admin/shop/article/update_form.html => byceps/blueprints/admin/shop/article/templates/admin/shop/article/update_form.html +1 -0
@@ 23,6 23,7 @@
      {{ form_datalist('tax_rates', ['0.00', '0.07', '0.19']) }}
      {{ form_field(form.available_from, size=16, maxlength=16, note='Format: <kbd>TT.MM.JJJJ hh:mm</kbd>'|safe) }}
      {{ form_field(form.available_until, size=16, maxlength=16, note='Format: <kbd>TT.MM.JJJJ hh:mm</kbd>'|safe) }}
      {{ form_field(form.total_quantity, type='number', min=0, max=99999, style='width: 5.5rem;', required='required') }}
      {{ form_field(form.max_quantity_per_order, type='number', min=1, max=99, style='width: 5.5rem;', required='required') }}
      {{ form_field_checkbox(form.not_directly_orderable, form.not_directly_orderable.label.text) }}
      {{ form_field_checkbox(form.requires_separate_order, form.requires_separate_order.label.text) }}

M byceps/blueprints/admin/shop/article/views.py => byceps/blueprints/admin/shop/article/views.py +5 -2
@@ 199,7 199,8 @@ def create(shop_id):
    description = form.description.data.strip()
    price = form.price.data
    tax_rate = form.tax_rate.data
    quantity = form.quantity.data
    total_quantity = form.total_quantity.data
    quantity = total_quantity
    max_quantity_per_order = form.max_quantity_per_order.data

    article = article_service.create_article(


@@ 208,7 209,7 @@ def create(shop_id):
        description,
        price,
        tax_rate,
        quantity,
        total_quantity,
        max_quantity_per_order,
    )



@@ 254,6 255,7 @@ def update(article_id):
    tax_rate = form.tax_rate.data
    available_from = form.available_from.data
    available_until = form.available_until.data
    total_quantity = form.total_quantity.data
    max_quantity_per_order = form.max_quantity_per_order.data
    not_directly_orderable = form.not_directly_orderable.data
    requires_separate_order = form.requires_separate_order.data


@@ 271,6 273,7 @@ def update(article_id):
        tax_rate,
        available_from,
        available_until,
        total_quantity,
        max_quantity_per_order,
        not_directly_orderable,
        requires_separate_order,

M byceps/services/shop/article/models/article.py => byceps/services/shop/article/models/article.py +4 -3
@@ 58,6 58,7 @@ class Article(db.Model):
    tax_rate = db.Column(db.Numeric(3, 3), nullable=False)
    available_from = db.Column(db.DateTime, nullable=True)
    available_until = db.Column(db.DateTime, nullable=True)
    total_quantity = db.Column(db.Integer, nullable=False)
    quantity = db.Column(db.Integer, db.CheckConstraint('quantity >= 0'), nullable=False)
    max_quantity_per_order = db.Column(db.Integer, nullable=False)
    not_directly_orderable = db.Column(db.Boolean, default=False, nullable=False)


@@ 71,7 72,7 @@ class Article(db.Model):
        description: str,
        price: Decimal,
        tax_rate: Decimal,
        quantity: int,
        total_quantity: int,
        max_quantity_per_order: int,
        *,
        available_from: Optional[datetime] = None,


@@ 84,7 85,8 @@ class Article(db.Model):
        self.tax_rate = tax_rate
        self.available_from = available_from
        self.available_until = available_until
        self.quantity = quantity
        self.total_quantity = total_quantity
        self.quantity = total_quantity  # Initialize with total quantity.
        self.max_quantity_per_order = max_quantity_per_order

    def __repr__(self) -> str:


@@ 93,5 95,4 @@ class Article(db.Model):
            .add('shop', self.shop_id) \
            .add_with_lookup('item_number') \
            .add_with_lookup('description') \
            .add_with_lookup('quantity') \
            .build()

M byceps/services/shop/article/service.py => byceps/services/shop/article/service.py +5 -2
@@ 39,7 39,7 @@ def create_article(
    description: str,
    price: Decimal,
    tax_rate: Decimal,
    quantity: int,
    total_quantity: int,
    max_quantity_per_order: int,
) -> Article:
    """Create an article."""


@@ 49,7 49,7 @@ def create_article(
        description,
        price,
        tax_rate,
        quantity,
        total_quantity,
        max_quantity_per_order,
    )



@@ 66,6 66,7 @@ def update_article(
    tax_rate: Decimal,
    available_from: Optional[datetime],
    available_until: Optional[datetime],
    total_quantity: int,
    max_quantity_per_order: int,
    not_directly_orderable: bool,
    requires_separate_order: bool,


@@ 79,6 80,7 @@ def update_article(
    article.tax_rate = tax_rate
    article.available_from = available_from
    article.available_until = available_until
    article.total_quantity = total_quantity
    article.max_quantity_per_order = max_quantity_per_order
    article.not_directly_orderable = not_directly_orderable
    article.requires_separate_order = requires_separate_order


@@ 406,6 408,7 @@ def _db_entity_to_article(article: DbArticle) -> Article:
        article.tax_rate,
        article.available_from,
        article.available_until,
        article.total_quantity,
        article.quantity,
        article.max_quantity_per_order,
        article.not_directly_orderable,

M byceps/services/shop/article/transfer/models.py => byceps/services/shop/article/transfer/models.py +1 -0
@@ 34,6 34,7 @@ class Article:
    tax_rate: Decimal
    available_from: Optional[datetime]
    available_until: Optional[datetime]
    total_quantity: int
    quantity: int
    max_quantity_per_order: int
    not_directly_orderable: bool

M testfixtures/shop_article.py => testfixtures/shop_article.py +4 -2
@@ 23,7 23,7 @@ def create_article(
    tax_rate=None,
    available_from=None,
    available_until=None,
    quantity=1,
    total_quantity=1,
    max_quantity_per_order=10,
):
    if price is None:


@@ 32,13 32,15 @@ def create_article(
    if tax_rate is None:
        tax_rate = Decimal('0.19')

    quantity = total_quantity

    return DbArticle(
        shop_id,
        item_number,
        description,
        price,
        tax_rate,
        quantity,
        total_quantity,
        max_quantity_per_order,
        available_from=available_from,
        available_until=available_until,

M tests/integration/blueprints/admin/shop/order/test_order_export.py => tests/integration/blueprints/admin/shop/order/test_order_export.py +1 -1
@@ 199,5 199,5 @@ def create_article(shop_id, item_number, description, price, tax_rate):
        description=description,
        price=price,
        tax_rate=tax_rate,
        quantity=10,
        total_quantity=10,
    )

M tests/integration/blueprints/admin/shop/order/test_views.py => tests/integration/blueprints/admin/shop/order/test_views.py +2 -2
@@ 288,12 288,12 @@ def test_cancel_after_paid(
# helpers


def create_article(shop_id, item_number, quantity):
def create_article(shop_id, item_number, total_quantity):
    return _create_article(
        shop_id,
        item_number=item_number,
        description=item_number,
        quantity=quantity,
        total_quantity=total_quantity,
    )



M tests/integration/blueprints/site/shop/order/test_views.py => tests/integration/blueprints/site/shop/order/test_views.py +1 -1
@@ 86,7 86,7 @@ def site_app(site, make_site_app):

@pytest.fixture
def article(admin_app, shop):
    article = create_article(shop.id, quantity=5)
    article = create_article(shop.id, total_quantity=5)
    article_id = article.id
    yield article
    article_service.delete_article(article_id)

M tests/integration/services/shop/helpers.py => tests/integration/services/shop/helpers.py +1 -1
@@ 34,6 34,6 @@ def create_article(shop_id, **kwargs):
        article.description,
        article.price,
        article.tax_rate,
        article.quantity,
        article.total_quantity,
        article.max_quantity_per_order,
    )

M tests/integration/services/shop/order/actions/conftest.py => tests/integration/services/shop/order/actions/conftest.py +1 -1
@@ 15,7 15,7 @@ from tests.integration.services.shop.helpers import create_article

@pytest.fixture
def article(shop):
    article = create_article(shop.id, quantity=10)
    article = create_article(shop.id, total_quantity=10)
    article_id = article.id
    yield article
    article_service.delete_article(article_id)

M tests/integration/services/shop/order/email/test_email_on_order_placed.py => tests/integration/services/shop/order/email/test_email_on_order_placed.py +2 -2
@@ 224,11 224,11 @@ E-Mail: noreply@acmecon.test
    )


def create_article(shop_id, item_number, description, price, quantity):
def create_article(shop_id, item_number, description, price, total_quantity):
    return _create_article(
        shop_id,
        item_number=item_number,
        description=description,
        price=price,
        quantity=quantity,
        total_quantity=total_quantity,
    )

M tests/integration/services/shop/order/models/test_order_total_amount.py => tests/integration/services/shop/order/models/test_order_total_amount.py +1 -1
@@ 96,7 96,7 @@ def create_article(shop_id, number, price):
        item_number=item_number,
        description=description,
        price=price,
        quantity=50,
        total_quantity=50,
    )



M tests/integration/services/shop/order/test_ordered_articles_service.py => tests/integration/services/shop/order/test_ordered_articles_service.py +1 -1
@@ 20,7 20,7 @@ from tests.integration.services.shop.helpers import create_article

@pytest.fixture
def article(shop):
    article = create_article(shop.id, quantity=100)
    article = create_article(shop.id, total_quantity=100)
    article_id = article.id
    yield article
    article_service.delete_article(article_id)

M tests/unit/services/shop/cart/test_cart_repr.py => tests/unit/services/shop/cart/test_cart_repr.py +2 -2
@@ 34,7 34,7 @@ def test_cart_filled_repr():

def create_article(item_number, description, price, tax_rate):
    shop_id = 'leshop'
    quantity = 99
    total_quantity = 99
    max_quantity_per_order = 10

    return DbArticle(


@@ 43,6 43,6 @@ def create_article(item_number, description, price, tax_rate):
        description,
        price,
        tax_rate,
        quantity,
        total_quantity,
        max_quantity_per_order,
    )