Files
bakery-ia/services/orders/README.md

960 lines
32 KiB
Markdown

# Orders Service
## Overview
The **Orders Service** manages the complete customer order lifecycle from creation to fulfillment, tracking custom orders, wholesale orders, and direct sales. It maintains a comprehensive customer database with purchase history, enables order scheduling for pickup/delivery, and provides analytics on customer behavior and order patterns. This service is essential for B2B relationships with restaurants and hotels, as well as managing special orders for events and celebrations.
## Key Features
### Order Management
- **Multi-Channel Orders** - In-store, phone, online, wholesale
- **Order Lifecycle Tracking** - From pending to completed/cancelled
- **Custom Orders** - Special requests for events and celebrations
- **Recurring Orders** - Automated weekly/monthly orders for B2B
- **Order Scheduling** - Pickup/delivery date and time management
- **Order Priority** - Rush orders vs. standard processing
- **Order Status Updates** - Real-time status with customer notifications
### Customer Database
- **Customer Profiles** - Complete contact and preference information
- **Purchase History** - Track all orders per customer
- **Customer Segmentation** - B2B vs. B2C, loyalty tiers
- **Customer Preferences** - Favorite products, allergen notes
- **Credit Terms** - Payment terms for wholesale customers
- **Customer Analytics** - RFM analysis (Recency, Frequency, Monetary)
- **Customer Lifetime Value** - Total value per customer
### B2B Wholesale Management
- **Wholesale Pricing** - Custom pricing per B2B customer
- **Volume Discounts** - Automatic tier-based discounts
- **Delivery Routes** - Optimize delivery scheduling
- **Invoice Generation** - Automated invoicing with payment terms
- **Standing Orders** - Repeat orders without manual entry
- **Account Management** - Credit limits and payment tracking
### Order Fulfillment
- **Production Integration** - Orders trigger production planning
- **Inventory Reservation** - Reserve stock for confirmed orders
- **Fulfillment Status** - Track preparation and delivery
- **Delivery Management** - Route planning and tracking
- **Order Picking Lists** - Generate lists for warehouse staff
- **Quality Control** - Pre-delivery quality checks
### Payment Tracking
- **Payment Methods** - Cash, card, transfer, credit terms
- **Payment Status** - Paid, pending, overdue
- **Partial Payments** - Split payments over time
- **Invoice History** - Complete payment records
- **Overdue Alerts** - Automatic reminders for B2B accounts
- **Revenue Recognition** - Track revenue per order
### Analytics & Reporting
- **Order Dashboard** - Real-time order metrics
- **Customer Analytics** - Top customers, retention rates
- **Product Analytics** - Most ordered products
- **Revenue Analytics** - Daily/weekly/monthly revenue
- **Order Source Analysis** - Channel performance
- **Delivery Performance** - On-time delivery rates
## Business Value
### For Bakery Owners
- **Revenue Growth** - Better customer relationships drive repeat business
- **B2B Efficiency** - Automate wholesale order management
- **Cash Flow** - Track outstanding payments and credit terms
- **Customer Retention** - Purchase history enables personalized service
- **Order Accuracy** - Digital orders reduce errors vs. phone/paper
- **Analytics** - Understand customer behavior for marketing
### Quantifiable Impact
- **Revenue Growth**: 10-20% through improved B2B relationships
- **Time Savings**: 5-8 hours/week on order management
- **Order Accuracy**: 99%+ vs. 85-90% manual (phone/paper)
- **Payment Collection**: 30% faster with automated reminders
- **Customer Retention**: 15-25% improvement with history tracking
- **B2B Efficiency**: 50-70% time reduction on wholesale orders
### For Sales Staff
- **Quick Order Entry** - Fast order creation with customer lookup
- **Customer History** - See previous orders for upselling
- **Pricing Accuracy** - Automatic wholesale pricing application
- **Order Tracking** - Know exactly when orders will be ready
- **Customer Notes** - Allergen info and preferences visible
### For Customers
- **Order Confirmation** - Immediate confirmation with details
- **Order Tracking** - Real-time status updates
- **Order History** - View and repeat previous orders
- **Flexible Scheduling** - Choose pickup/delivery times
- **Payment Options** - Multiple payment methods
## Technology Stack
- **Framework**: FastAPI (Python 3.11+) - Async web framework
- **Database**: PostgreSQL 17 - Order and customer data
- **Caching**: Redis 7.4 - Customer and order cache
- **Messaging**: RabbitMQ 4.1 - Order event publishing
- **ORM**: SQLAlchemy 2.0 (async) - Database abstraction
- **Validation**: Pydantic 2.0 - Schema validation
- **Logging**: Structlog - Structured JSON logging
- **Metrics**: Prometheus Client - Order metrics
## API Endpoints (Key Routes)
### Order Management
- `GET /api/v1/orders` - List orders with filters
- `POST /api/v1/orders` - Create new order
- `GET /api/v1/orders/{order_id}` - Get order details
- `PUT /api/v1/orders/{order_id}` - Update order
- `DELETE /api/v1/orders/{order_id}` - Cancel order
- `PUT /api/v1/orders/{order_id}/status` - Update order status
- `POST /api/v1/orders/{order_id}/complete` - Mark order complete
### Order Items
- `GET /api/v1/orders/{order_id}/items` - List order items
- `POST /api/v1/orders/{order_id}/items` - Add item to order
- `PUT /api/v1/orders/{order_id}/items/{item_id}` - Update order item
- `DELETE /api/v1/orders/{order_id}/items/{item_id}` - Remove item
### Customer Management
- `GET /api/v1/customers` - List customers with filters
- `POST /api/v1/customers` - Create new customer
- `GET /api/v1/customers/{customer_id}` - Get customer details
- `PUT /api/v1/customers/{customer_id}` - Update customer
- `GET /api/v1/customers/{customer_id}/orders` - Get customer order history
- `GET /api/v1/customers/{customer_id}/analytics` - Customer analytics
### Wholesale Management
- `GET /api/v1/orders/wholesale` - List wholesale orders
- `POST /api/v1/orders/wholesale/recurring` - Create recurring order
- `GET /api/v1/orders/wholesale/invoices` - List invoices
- `POST /api/v1/orders/wholesale/invoices/{invoice_id}/send` - Send invoice
- `GET /api/v1/orders/wholesale/overdue` - List overdue payments
### Fulfillment
- `GET /api/v1/orders/fulfillment/pending` - Orders pending fulfillment
- `POST /api/v1/orders/{order_id}/prepare` - Start order preparation
- `POST /api/v1/orders/{order_id}/ready` - Mark order ready
- `POST /api/v1/orders/{order_id}/deliver` - Mark order delivered
- `GET /api/v1/orders/fulfillment/picking-list` - Generate picking list
### Analytics
- `GET /api/v1/orders/analytics/dashboard` - Order dashboard KPIs
- `GET /api/v1/orders/analytics/revenue` - Revenue analytics
- `GET /api/v1/orders/analytics/customers/top` - Top customers
- `GET /api/v1/orders/analytics/products/popular` - Most ordered products
- `GET /api/v1/orders/analytics/channels` - Order channel breakdown
## Database Schema
### Main Tables
**customers**
```sql
CREATE TABLE customers (
id UUID PRIMARY KEY,
tenant_id UUID NOT NULL,
customer_type VARCHAR(50) NOT NULL, -- retail, wholesale, restaurant, hotel
business_name VARCHAR(255), -- For B2B customers
contact_name VARCHAR(255) NOT NULL,
email VARCHAR(255),
phone VARCHAR(50) NOT NULL,
secondary_phone VARCHAR(50),
address_line1 VARCHAR(255),
address_line2 VARCHAR(255),
city VARCHAR(100),
postal_code VARCHAR(20),
country VARCHAR(100) DEFAULT 'España',
tax_id VARCHAR(50), -- CIF/NIF for businesses
credit_limit DECIMAL(10, 2), -- For B2B customers
credit_term_days INTEGER DEFAULT 0, -- Payment terms (e.g., Net 30)
payment_status VARCHAR(50) DEFAULT 'good_standing', -- good_standing, overdue, suspended
customer_notes TEXT,
allergen_notes TEXT,
preferred_contact_method VARCHAR(50), -- email, phone, whatsapp
loyalty_tier VARCHAR(50) DEFAULT 'standard', -- standard, silver, gold, platinum
total_lifetime_value DECIMAL(12, 2) DEFAULT 0.00,
total_orders INTEGER DEFAULT 0,
last_order_date DATE,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW(),
UNIQUE(tenant_id, email),
UNIQUE(tenant_id, phone)
);
```
**orders**
```sql
CREATE TABLE orders (
id UUID PRIMARY KEY,
tenant_id UUID NOT NULL,
order_number VARCHAR(100) NOT NULL, -- Human-readable order number
customer_id UUID REFERENCES customers(id),
order_type VARCHAR(50) NOT NULL, -- retail, wholesale, custom, standing
order_source VARCHAR(50), -- in_store, phone, online, email
status VARCHAR(50) DEFAULT 'pending', -- pending, confirmed, preparing, ready, completed, cancelled
priority VARCHAR(50) DEFAULT 'standard', -- rush, standard, scheduled
order_date DATE NOT NULL DEFAULT CURRENT_DATE,
requested_date DATE, -- Pickup/delivery date
requested_time TIME, -- Pickup/delivery time
fulfilled_date DATE,
subtotal DECIMAL(10, 2) NOT NULL DEFAULT 0.00,
discount_amount DECIMAL(10, 2) DEFAULT 0.00,
tax_amount DECIMAL(10, 2) DEFAULT 0.00,
total_amount DECIMAL(10, 2) NOT NULL DEFAULT 0.00,
payment_method VARCHAR(50), -- cash, card, transfer, credit
payment_status VARCHAR(50) DEFAULT 'unpaid', -- unpaid, paid, partial, overdue
payment_due_date DATE,
delivery_method VARCHAR(50), -- pickup, delivery, shipping
delivery_address TEXT,
delivery_notes TEXT,
internal_notes TEXT,
created_by UUID NOT NULL,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW(),
UNIQUE(tenant_id, order_number)
);
```
**order_items**
```sql
CREATE TABLE order_items (
id UUID PRIMARY KEY,
tenant_id UUID NOT NULL,
order_id UUID REFERENCES orders(id) ON DELETE CASCADE,
product_id UUID NOT NULL,
product_name VARCHAR(255) NOT NULL, -- Cached for performance
quantity DECIMAL(10, 2) NOT NULL,
unit VARCHAR(50) NOT NULL,
unit_price DECIMAL(10, 2) NOT NULL,
discount_percentage DECIMAL(5, 2) DEFAULT 0.00,
line_total DECIMAL(10, 2) NOT NULL,
custom_instructions TEXT,
recipe_id UUID, -- Link to recipe if applicable
production_batch_id UUID, -- Link to production batch
fulfilled_quantity DECIMAL(10, 2) DEFAULT 0.00,
fulfillment_status VARCHAR(50) DEFAULT 'pending', -- pending, reserved, prepared, fulfilled
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
```
**customer_pricing**
```sql
CREATE TABLE customer_pricing (
id UUID PRIMARY KEY,
tenant_id UUID NOT NULL,
customer_id UUID REFERENCES customers(id) ON DELETE CASCADE,
product_id UUID NOT NULL,
custom_price DECIMAL(10, 2) NOT NULL,
discount_percentage DECIMAL(5, 2),
min_quantity DECIMAL(10, 2), -- Minimum order quantity for price
valid_from DATE DEFAULT CURRENT_DATE,
valid_until DATE,
is_active BOOLEAN DEFAULT TRUE,
notes TEXT,
created_at TIMESTAMP DEFAULT NOW(),
UNIQUE(tenant_id, customer_id, product_id)
);
```
**recurring_orders**
```sql
CREATE TABLE recurring_orders (
id UUID PRIMARY KEY,
tenant_id UUID NOT NULL,
customer_id UUID REFERENCES customers(id) ON DELETE CASCADE,
recurring_name VARCHAR(255) NOT NULL,
frequency VARCHAR(50) NOT NULL, -- daily, weekly, biweekly, monthly
delivery_day VARCHAR(50), -- Monday, Tuesday, etc.
delivery_time TIME,
order_items JSONB NOT NULL, -- Array of {product_id, quantity, unit}
is_active BOOLEAN DEFAULT TRUE,
next_order_date DATE,
last_generated_order_id UUID,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
```
**order_status_history**
```sql
CREATE TABLE order_status_history (
id UUID PRIMARY KEY,
tenant_id UUID NOT NULL,
order_id UUID REFERENCES orders(id) ON DELETE CASCADE,
from_status VARCHAR(50),
to_status VARCHAR(50) NOT NULL,
changed_by UUID NOT NULL,
notes TEXT,
changed_at TIMESTAMP DEFAULT NOW()
);
```
**invoices**
```sql
CREATE TABLE invoices (
id UUID PRIMARY KEY,
tenant_id UUID NOT NULL,
invoice_number VARCHAR(100) NOT NULL,
order_id UUID REFERENCES orders(id),
customer_id UUID REFERENCES customers(id),
invoice_date DATE NOT NULL DEFAULT CURRENT_DATE,
due_date DATE NOT NULL,
subtotal DECIMAL(10, 2) NOT NULL,
tax_amount DECIMAL(10, 2) NOT NULL,
total_amount DECIMAL(10, 2) NOT NULL,
amount_paid DECIMAL(10, 2) DEFAULT 0.00,
amount_due DECIMAL(10, 2) NOT NULL,
status VARCHAR(50) DEFAULT 'sent', -- draft, sent, paid, overdue, cancelled
payment_terms VARCHAR(255),
notes TEXT,
sent_at TIMESTAMP,
paid_at TIMESTAMP,
created_at TIMESTAMP DEFAULT NOW(),
UNIQUE(tenant_id, invoice_number)
);
```
### Indexes for Performance
```sql
CREATE INDEX idx_orders_tenant_status ON orders(tenant_id, status);
CREATE INDEX idx_orders_customer ON orders(customer_id);
CREATE INDEX idx_orders_date ON orders(tenant_id, order_date DESC);
CREATE INDEX idx_orders_requested_date ON orders(tenant_id, requested_date);
CREATE INDEX idx_customers_tenant_type ON customers(tenant_id, customer_type);
CREATE INDEX idx_order_items_order ON order_items(order_id);
CREATE INDEX idx_order_items_product ON order_items(tenant_id, product_id);
CREATE INDEX idx_invoices_status ON invoices(tenant_id, status);
CREATE INDEX idx_invoices_due_date ON invoices(tenant_id, due_date) WHERE status != 'paid';
```
## Business Logic Examples
### Order Creation with Pricing
```python
async def create_order(order_data: OrderCreate, current_user: User) -> Order:
"""
Create new order with automatic pricing and customer detection.
"""
# Get or create customer
customer = await get_or_create_customer(
order_data.customer_phone,
order_data.customer_name,
order_data.customer_email
)
# Generate order number
order_number = await generate_order_number(current_user.tenant_id)
# Create order
order = Order(
tenant_id=current_user.tenant_id,
order_number=order_number,
customer_id=customer.id,
order_type=order_data.order_type,
order_source=order_data.order_source,
status='pending',
order_date=date.today(),
requested_date=order_data.requested_date,
created_by=current_user.id
)
db.add(order)
await db.flush() # Get order.id
# Add order items with pricing
subtotal = Decimal('0.00')
for item_data in order_data.items:
# Get product price
base_price = await get_product_price(item_data.product_id)
# Check for customer-specific pricing
custom_price = await get_customer_price(
customer.id,
item_data.product_id,
item_data.quantity
)
unit_price = custom_price if custom_price else base_price
# Apply wholesale discount if applicable
if customer.customer_type == 'wholesale':
discount_pct = await calculate_volume_discount(
item_data.product_id,
item_data.quantity
)
else:
discount_pct = Decimal('0.00')
# Calculate line total
line_total = (unit_price * item_data.quantity) * (1 - discount_pct / 100)
# Create order item
order_item = OrderItem(
tenant_id=current_user.tenant_id,
order_id=order.id,
product_id=item_data.product_id,
product_name=item_data.product_name,
quantity=item_data.quantity,
unit=item_data.unit,
unit_price=unit_price,
discount_percentage=discount_pct,
line_total=line_total
)
db.add(order_item)
subtotal += line_total
# Calculate tax (e.g., Spanish IVA 10% for food)
tax_rate = Decimal('0.10')
tax_amount = subtotal * tax_rate
total_amount = subtotal + tax_amount
# Update order totals
order.subtotal = subtotal
order.tax_amount = tax_amount
order.total_amount = total_amount
# Set payment terms for B2B
if customer.customer_type == 'wholesale':
order.payment_due_date = date.today() + timedelta(days=customer.credit_term_days)
order.payment_status = 'unpaid'
else:
order.payment_status = 'paid' # Retail assumes immediate payment
await db.commit()
await db.refresh(order)
# Publish order created event
await publish_event('orders', 'order.created', {
'order_id': str(order.id),
'customer_id': str(customer.id),
'total_amount': float(order.total_amount),
'requested_date': order.requested_date.isoformat() if order.requested_date else None
})
return order
```
### Recurring Order Generation
```python
async def generate_recurring_orders(tenant_id: UUID):
"""
Generate orders from recurring order templates.
Run daily via orchestrator.
"""
# Get active recurring orders due today
today = date.today()
recurring_orders = await db.query(RecurringOrder).filter(
RecurringOrder.tenant_id == tenant_id,
RecurringOrder.is_active == True,
RecurringOrder.next_order_date <= today
).all()
generated_count = 0
for recurring in recurring_orders:
try:
# Create order from template
order = Order(
tenant_id=tenant_id,
order_number=await generate_order_number(tenant_id),
customer_id=recurring.customer_id,
order_type='standing',
order_source='auto_recurring',
status='confirmed',
order_date=today,
requested_date=recurring.next_order_date,
requested_time=recurring.delivery_time
)
db.add(order)
await db.flush()
# Add items from template
subtotal = Decimal('0.00')
for item_template in recurring.order_items:
product_price = await get_product_price(item_template['product_id'])
line_total = product_price * Decimal(str(item_template['quantity']))
order_item = OrderItem(
tenant_id=tenant_id,
order_id=order.id,
product_id=UUID(item_template['product_id']),
product_name=item_template['product_name'],
quantity=Decimal(str(item_template['quantity'])),
unit=item_template['unit'],
unit_price=product_price,
line_total=line_total
)
db.add(order_item)
subtotal += line_total
# Calculate totals
tax_amount = subtotal * Decimal('0.10')
order.subtotal = subtotal
order.tax_amount = tax_amount
order.total_amount = subtotal + tax_amount
# Update recurring order
recurring.last_generated_order_id = order.id
recurring.next_order_date = calculate_next_order_date(
recurring.next_order_date,
recurring.frequency
)
await db.commit()
generated_count += 1
# Publish event
await publish_event('orders', 'recurring_order.generated', {
'order_id': str(order.id),
'recurring_order_id': str(recurring.id),
'customer_id': str(recurring.customer_id)
})
except Exception as e:
logger.error("Failed to generate recurring order",
recurring_id=str(recurring.id),
error=str(e))
continue
logger.info("Generated recurring orders",
tenant_id=str(tenant_id),
count=generated_count)
return generated_count
```
### Customer RFM Analysis
```python
async def calculate_customer_rfm(customer_id: UUID) -> dict:
"""
Calculate RFM (Recency, Frequency, Monetary) metrics for customer.
"""
# Get customer orders
orders = await db.query(Order).filter(
Order.customer_id == customer_id,
Order.status.in_(['completed'])
).order_by(Order.order_date.desc()).all()
if not orders:
return {"rfm_score": 0, "segment": "inactive"}
# Recency: Days since last order
last_order_date = orders[0].order_date
recency_days = (date.today() - last_order_date).days
# Frequency: Number of orders in last 365 days
one_year_ago = date.today() - timedelta(days=365)
recent_orders = [o for o in orders if o.order_date >= one_year_ago]
frequency = len(recent_orders)
# Monetary: Total spend in last 365 days
monetary = sum(o.total_amount for o in recent_orders)
# Score each dimension (1-5 scale)
recency_score = 5 if recency_days <= 30 else \
4 if recency_days <= 60 else \
3 if recency_days <= 90 else \
2 if recency_days <= 180 else 1
frequency_score = 5 if frequency >= 12 else \
4 if frequency >= 6 else \
3 if frequency >= 3 else \
2 if frequency >= 1 else 1
monetary_score = 5 if monetary >= 5000 else \
4 if monetary >= 2000 else \
3 if monetary >= 500 else \
2 if monetary >= 100 else 1
# Overall RFM score
rfm_score = (recency_score + frequency_score + monetary_score) / 3
# Customer segment
if rfm_score >= 4.5:
segment = "champion"
elif rfm_score >= 3.5:
segment = "loyal"
elif rfm_score >= 2.5:
segment = "potential"
elif rfm_score >= 1.5:
segment = "at_risk"
else:
segment = "inactive"
return {
"rfm_score": round(rfm_score, 2),
"recency_days": recency_days,
"recency_score": recency_score,
"frequency": frequency,
"frequency_score": frequency_score,
"monetary": float(monetary),
"monetary_score": monetary_score,
"segment": segment
}
```
## Events & Messaging
### Published Events (RabbitMQ)
**Exchange**: `orders`
**Routing Keys**: `orders.created`, `orders.completed`, `orders.cancelled`, `orders.overdue`
**Order Created Event**
```json
{
"event_type": "order_created",
"tenant_id": "uuid",
"order_id": "uuid",
"order_number": "ORD-2025-1106-001",
"customer_id": "uuid",
"customer_name": "Restaurante El Prado",
"order_type": "wholesale",
"total_amount": 450.00,
"requested_date": "2025-11-07",
"requested_time": "06:00:00",
"item_count": 12,
"timestamp": "2025-11-06T10:30:00Z"
}
```
**Order Completed Event**
```json
{
"event_type": "order_completed",
"tenant_id": "uuid",
"order_id": "uuid",
"order_number": "ORD-2025-1106-001",
"customer_id": "uuid",
"total_amount": 450.00,
"payment_status": "paid",
"completed_at": "2025-11-07T06:15:00Z",
"timestamp": "2025-11-07T06:15:00Z"
}
```
**Payment Overdue Alert**
```json
{
"event_type": "payment_overdue",
"tenant_id": "uuid",
"invoice_id": "uuid",
"invoice_number": "INV-2025-1106-001",
"customer_id": "uuid",
"customer_name": "Hotel Gran Vía",
"amount_due": 850.00,
"days_overdue": 15,
"due_date": "2025-10-22",
"timestamp": "2025-11-06T09:00:00Z"
}
```
### Alert Events
The Orders service also publishes procurement-related alerts through the alert processor.
**Exchange**: `events.exchange`
**Domain**: `procurement`
#### 1. POs Pending Approval Alert
**Event Type**: `procurement.pos_pending_approval`
**Severity**: urgent (>€10,000), high (>€5,000 or critical POs), medium (otherwise)
**Trigger**: New purchase orders created and awaiting approval
```json
{
"event_type": "procurement.pos_pending_approval",
"severity": "high",
"metadata": {
"tenant_id": "uuid",
"pos_count": 3,
"total_amount": 6500.00,
"critical_count": 1,
"pos": [
{
"po_id": "uuid",
"po_number": "PO-001",
"supplier_id": "uuid",
"total_amount": 3000.00,
"auto_approved": false
}
],
"action_required": true,
"action_url": "/app/comprar"
}
}
```
#### 2. Approval Reminder Alert
**Event Type**: `procurement.approval_reminder`
**Severity**: high (>36 hours pending), medium (otherwise)
**Trigger**: PO not approved within threshold time
```json
{
"event_type": "procurement.approval_reminder",
"severity": "high",
"metadata": {
"tenant_id": "uuid",
"po_id": "uuid",
"po_number": "PO-001",
"supplier_name": "Supplier ABC",
"total_amount": 3000.00,
"hours_pending": 40,
"created_at": "2025-12-18T10:00:00Z",
"action_required": true,
"action_url": "/app/comprar?po=uuid"
}
}
```
#### 3. Critical PO Escalation Alert
**Event Type**: `procurement.critical_po_escalation`
**Severity**: urgent
**Trigger**: Critical/urgent PO not approved in time
```json
{
"event_type": "procurement.critical_po_escalation",
"severity": "urgent",
"metadata": {
"tenant_id": "uuid",
"po_id": "uuid",
"po_number": "PO-001",
"supplier_name": "Supplier ABC",
"total_amount": 5000.00,
"priority": "urgent",
"required_delivery_date": "2025-12-22",
"hours_pending": 48,
"escalated": true,
"action_required": true,
"action_url": "/app/comprar?po=uuid"
}
}
```
#### 4. Auto-Approval Summary (Notification)
**Event Type**: `procurement.auto_approval_summary`
**Type**: Notification (not alert)
**Trigger**: Daily summary of auto-approved POs
```json
{
"event_type": "procurement.auto_approval_summary",
"metadata": {
"tenant_id": "uuid",
"auto_approved_count": 5,
"total_auto_approved_amount": 8500.00,
"manual_approval_count": 2,
"summary_date": "2025-12-19",
"auto_approved_pos": [...],
"pending_approval_pos": [...],
"action_url": "/app/comprar"
}
}
```
#### 5. PO Approved Confirmation (Notification)
**Event Type**: `procurement.po_approved_confirmation`
**Type**: Notification (not alert)
**Trigger**: Purchase order approved
```json
{
"event_type": "procurement.po_approved_confirmation",
"metadata": {
"tenant_id": "uuid",
"po_id": "uuid",
"po_number": "PO-001",
"supplier_name": "Supplier ABC",
"total_amount": 3000.00,
"approved_by": "user@example.com",
"auto_approved": false,
"approved_at": "2025-12-19T14:30:00Z",
"action_url": "/app/comprar?po=uuid"
}
}
```
### Consumed Events
- **From Production**: Batch completion updates order fulfillment status
- **From Inventory**: Stock availability affects order confirmation
- **From Forecasting**: Demand forecasts inform production for pending orders
## Custom Metrics (Prometheus)
```python
# Order metrics
orders_total = Counter(
'orders_total',
'Total orders created',
['tenant_id', 'order_type', 'order_source', 'status']
)
order_value_euros = Histogram(
'order_value_euros',
'Order value distribution',
['tenant_id', 'order_type'],
buckets=[10, 25, 50, 100, 200, 500, 1000, 2000, 5000]
)
# Customer metrics
customers_total = Gauge(
'customers_total',
'Total customers',
['tenant_id', 'customer_type']
)
customer_lifetime_value_euros = Histogram(
'customer_lifetime_value_euros',
'Customer lifetime value distribution',
['tenant_id', 'customer_type'],
buckets=[100, 500, 1000, 2000, 5000, 10000, 20000, 50000]
)
# Fulfillment metrics
order_fulfillment_time_hours = Histogram(
'order_fulfillment_time_hours',
'Time from order to fulfillment',
['tenant_id', 'order_type'],
buckets=[1, 6, 12, 24, 48, 72]
)
# Payment metrics
invoice_payment_time_days = Histogram(
'invoice_payment_time_days',
'Days from invoice to payment',
['tenant_id'],
buckets=[0, 7, 14, 21, 30, 45, 60, 90]
)
overdue_invoices_total = Gauge(
'overdue_invoices_total',
'Total overdue invoices',
['tenant_id']
)
```
## Configuration
### Environment Variables
**Service Configuration:**
- `PORT` - Service port (default: 8010)
- `DATABASE_URL` - PostgreSQL connection string
- `REDIS_URL` - Redis connection string
- `RABBITMQ_URL` - RabbitMQ connection string
**Order Configuration:**
- `AUTO_CONFIRM_RETAIL_ORDERS` - Auto-confirm retail orders (default: true)
- `ORDER_NUMBER_PREFIX` - Order number prefix (default: "ORD")
- `DEFAULT_TAX_RATE` - Default tax rate (default: 0.10 for Spain's 10% IVA)
- `ENABLE_RECURRING_ORDERS` - Enable recurring order generation (default: true)
**Payment Configuration:**
- `DEFAULT_CREDIT_TERMS_DAYS` - Default payment terms (default: 30)
- `OVERDUE_ALERT_THRESHOLD_DAYS` - Days before overdue alert (default: 7)
- `MAX_CREDIT_LIMIT` - Maximum credit limit per customer (default: 10000.00)
**Notification:**
- `SEND_ORDER_CONFIRMATION` - Send order confirmation to customer (default: true)
- `SEND_READY_NOTIFICATION` - Notify when order ready (default: true)
- `SEND_OVERDUE_REMINDERS` - Send overdue payment reminders (default: true)
## Development Setup
### Prerequisites
- Python 3.11+
- PostgreSQL 17
- Redis 7.4
- RabbitMQ 4.1
### Local Development
```bash
cd services/orders
python -m venv venv
source venv/bin/activate
pip install -r requirements.txt
export DATABASE_URL=postgresql://user:pass@localhost:5432/orders
export REDIS_URL=redis://localhost:6379/0
export RABBITMQ_URL=amqp://guest:guest@localhost:5672/
alembic upgrade head
python main.py
```
## Integration Points
### Dependencies
- **Customers Service** - Customer data (if separate)
- **Products Service** - Product catalog and pricing
- **Inventory Service** - Stock availability checks
- **Production Service** - Production planning for orders
- **Auth Service** - User authentication
- **PostgreSQL** - Order and customer data
- **Redis** - Caching
- **RabbitMQ** - Event publishing
### Dependents
- **Production Service** - Orders trigger production planning
- **Inventory Service** - Orders reserve stock
- **Invoicing/Accounting** - Financial reporting
- **Notification Service** - Order confirmations and alerts
- **AI Insights Service** - Customer behavior analysis
- **Frontend Dashboard** - Order management UI
## Business Value for VUE Madrid
### Problem Statement
Spanish bakeries struggle with:
- Manual order tracking on paper or spreadsheets
- Lost orders and miscommunication (especially phone orders)
- No customer purchase history for relationship management
- Complex wholesale order management with multiple B2B clients
- Overdue payment tracking for credit accounts
- No analytics on customer behavior or product popularity
### Solution
Bakery-IA Orders Service provides:
- **Digital Order Management**: Capture all orders across channels
- **Customer Database**: Complete purchase history and preferences
- **B2B Automation**: Recurring orders and automated invoicing
- **Payment Tracking**: Monitor outstanding payments with alerts
- **Analytics**: Customer segmentation and product performance
### Quantifiable Impact
**Revenue Growth:**
- 10-20% revenue increase through improved B2B relationships
- 5-10% from reduced lost orders (99% order accuracy)
- 15-25% customer retention improvement with history tracking
- **Total: €300-600/month additional revenue per bakery**
**Time Savings:**
- 5-8 hours/week on order management and tracking
- 2-3 hours/week on invoicing and payment follow-up
- 1-2 hours/week on customer lookup and history
- **Total: 8-13 hours/week saved**
**Financial Performance:**
- 30% faster payment collection (overdue alerts)
- 50-70% time reduction on wholesale order processing
- 99%+ order accuracy vs. 85-90% manual
### Target Market Fit (Spanish Bakeries)
- **B2B Focus**: Many Spanish bakeries supply restaurants, hotels, cafés
- **Payment Terms**: Spanish B2B typically uses Net 30-60 payment terms
- **Relationship-Driven**: Customer history critical for Spanish business culture
- **Regulatory**: Spanish tax law requires proper invoicing and records
### ROI Calculation
**Investment**: €0 additional (included in platform subscription)
**Monthly Value**: €300-600 additional revenue + cost savings
**Annual ROI**: €3,600-7,200 value per bakery
**Payback**: Immediate (included in subscription)
---
**Copyright © 2025 Bakery-IA. All rights reserved.**