feat: add VAT tax rate, cost, and Letzshop feed settings

Product Model:
- Add tax_rate_percent (NOT NULL, default 17) for Luxembourg VAT
- Add cost_cents for profit calculation
- Add profit calculation properties: net_price, vat_amount, profit, margin
- Rename supplier_cost_cents to cost_cents

MarketplaceProduct Model:
- Add tax_rate_percent (NOT NULL, default 17)

Vendor Model (Letzshop feed settings):
- letzshop_default_tax_rate: Default VAT for new products (0, 3, 8, 14, 17)
- letzshop_boost_sort: Product sort priority (0.0-10.0)
- letzshop_delivery_method: nationwide, package_delivery, self_collect
- letzshop_preorder_days: Pre-order shipping delay

VAT Strategy:
- Store prices as gross (VAT-inclusive) for B2C
- Calculate net from gross when needed for profit
- Luxembourg VAT rates: 0%, 3%, 8%, 14%, 17%

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-20 21:15:47 +01:00
parent a19c84ea4e
commit 8a2a955c92
5 changed files with 270 additions and 11 deletions

View File

@@ -0,0 +1,64 @@
"""add_tax_rate_cost_and_letzshop_settings
Revision ID: c9e22eadf533
Revises: e1f2a3b4c5d6
Create Date: 2025-12-20 21:13:30.709696
Adds:
- tax_rate_percent to products and marketplace_products (NOT NULL, default 17)
- cost_cents to products (for profit calculation)
- Letzshop feed settings to vendors (tax_rate, boost_sort, delivery_method, preorder_days)
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = 'c9e22eadf533'
down_revision: Union[str, None] = 'e1f2a3b4c5d6'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
# === MARKETPLACE PRODUCTS: Add tax_rate_percent ===
with op.batch_alter_table('marketplace_products', schema=None) as batch_op:
batch_op.add_column(sa.Column('tax_rate_percent', sa.Integer(), nullable=False, server_default='17'))
# === PRODUCTS: Add tax_rate_percent and cost_cents, rename supplier_cost_cents ===
with op.batch_alter_table('products', schema=None) as batch_op:
batch_op.add_column(sa.Column('tax_rate_percent', sa.Integer(), nullable=False, server_default='17'))
batch_op.add_column(sa.Column('cost_cents', sa.Integer(), nullable=True))
# Drop old supplier_cost_cents column (data migrated to cost_cents if needed)
try:
batch_op.drop_column('supplier_cost_cents')
except Exception:
pass # Column may not exist
# === VENDORS: Add Letzshop feed settings ===
with op.batch_alter_table('vendors', schema=None) as batch_op:
batch_op.add_column(sa.Column('letzshop_default_tax_rate', sa.Integer(), nullable=False, server_default='17'))
batch_op.add_column(sa.Column('letzshop_boost_sort', sa.String(length=10), nullable=True, server_default='5.0'))
batch_op.add_column(sa.Column('letzshop_delivery_method', sa.String(length=100), nullable=True, server_default='package_delivery'))
batch_op.add_column(sa.Column('letzshop_preorder_days', sa.Integer(), nullable=True, server_default='1'))
def downgrade() -> None:
# === VENDORS: Remove Letzshop feed settings ===
with op.batch_alter_table('vendors', schema=None) as batch_op:
batch_op.drop_column('letzshop_preorder_days')
batch_op.drop_column('letzshop_delivery_method')
batch_op.drop_column('letzshop_boost_sort')
batch_op.drop_column('letzshop_default_tax_rate')
# === PRODUCTS: Remove tax_rate_percent and cost_cents ===
with op.batch_alter_table('products', schema=None) as batch_op:
batch_op.drop_column('cost_cents')
batch_op.drop_column('tax_rate_percent')
batch_op.add_column(sa.Column('supplier_cost_cents', sa.Integer(), nullable=True))
# === MARKETPLACE PRODUCTS: Remove tax_rate_percent ===
with op.batch_alter_table('marketplace_products', schema=None) as batch_op:
batch_op.drop_column('tax_rate_percent')