A comprehensive Customer Relationship Management software built on Django with extensive customization capabilities
Comprehensive billing system with invoice generation, quotations, credit notes, sales orders, and financial document workflows. Includes PDF generation, email integration, and payment tracking.
Invoice creation and management for billing customers.
class Invoice(CremeEntity):
"""
Invoice entity for billing customers with line items and tax calculations.
Attributes:
- name: str, invoice reference/number
- number: int, sequential invoice number
- issuing_date: DateField, invoice issue date
- expiration_date: DateField, payment due date
- discount: DecimalField, overall discount percentage
- additional_info: TextField, additional notes
- payment_terms: TextField, payment terms and conditions
- payment_type: int, payment method
- currency: ForeignKey, invoice currency
- total_vat: DecimalField, total VAT amount
- total_no_vat: DecimalField, subtotal before VAT
- status: ForeignKey, invoice status
- buyers_order_number: str, customer purchase order reference
- source: ForeignKey, related quote or sales order
Methods:
- get_absolute_url(): Get invoice detail URL
- get_total(): Calculate total amount including VAT
- get_total_with_tax(): Get total with all taxes
- get_lines(): Get invoice line items
- generate_number(): Generate sequential invoice number
- build_pdf(): Generate PDF document
- send_email(): Email invoice to customer
"""
def get_invoice_model():
"""
Get the active Invoice model class from settings.
Returns:
type[Invoice]: The configured invoice model class
Example:
InvoiceModel = get_invoice_model()
invoice = InvoiceModel.objects.create(
name="INV-2023-001",
issuing_date=date.today()
)
"""Quotation system for price estimates and proposals.
class Quote(CremeEntity):
"""
Quotation entity for providing price estimates to prospects.
Attributes:
- name: str, quote reference/number
- number: int, sequential quote number
- issuing_date: DateField, quote creation date
- expiration_date: DateField, quote expiration date
- acceptation_date: DateField, date quote was accepted
- discount: DecimalField, overall discount percentage
- additional_info: TextField, additional notes
- payment_terms: TextField, payment terms
- currency: ForeignKey, quote currency
- total_vat: DecimalField, total VAT amount
- total_no_vat: DecimalField, subtotal before VAT
- status: ForeignKey, quote status
Methods:
- get_absolute_url(): Get quote detail URL
- get_total(): Calculate total amount
- convert_to_invoice(): Convert accepted quote to invoice
- convert_to_sales_order(): Convert to sales order
- is_expired(): Check if quote has expired
- accept(): Mark quote as accepted
"""
def get_quote_model():
"""
Get the active Quote model class from settings.
Returns:
type[Quote]: The configured quote model class
"""Credit note system for refunds and billing corrections.
class CreditNote(CremeEntity):
"""
Credit note entity for refunds and billing corrections.
Attributes:
- name: str, credit note reference/number
- number: int, sequential credit note number
- issuing_date: DateField, issue date
- discount: DecimalField, discount percentage
- additional_info: TextField, reason for credit
- currency: ForeignKey, currency
- total_vat: DecimalField, total VAT amount
- total_no_vat: DecimalField, subtotal before VAT
- status: ForeignKey, credit note status
Methods:
- get_absolute_url(): Get credit note detail URL
- get_total(): Calculate total credit amount
- apply_to_invoice(invoice): Apply credit to specific invoice
"""
def get_creditnote_model():
"""
Get the active CreditNote model class from settings.
Returns:
type[CreditNote]: The configured credit note model class
"""Sales order processing between quotes and invoices.
class SalesOrder(CremeEntity):
"""
Sales order entity for managing confirmed orders before invoicing.
Attributes:
- name: str, sales order reference/number
- number: int, sequential order number
- issuing_date: DateField, order creation date
- expected_delivery_date: DateField, expected delivery
- discount: DecimalField, discount percentage
- additional_info: TextField, order notes
- payment_terms: TextField, payment terms
- currency: ForeignKey, order currency
- total_vat: DecimalField, total VAT amount
- total_no_vat: DecimalField, subtotal before VAT
- status: ForeignKey, order status
Methods:
- get_absolute_url(): Get sales order detail URL
- get_total(): Calculate total order amount
- convert_to_invoice(): Convert to invoice
- ship(): Mark order as shipped
- complete(): Mark order as completed
"""
def get_salesorder_model():
"""
Get the active SalesOrder model class from settings.
Returns:
type[SalesOrder]: The configured sales order model class
"""Product and service line items for billing documents.
class ProductLine(CremeEntity):
"""
Product line item for billing documents.
Attributes:
- on_the_fly_item: str, custom product description
- comment: TextField, line item notes
- quantity: DecimalField, item quantity
- unit_price: DecimalField, price per unit
- unit: str, unit of measurement
- discount: DecimalField, line discount percentage
- discount_unit: int, discount type (percentage/amount)
- vat_value: ForeignKey, VAT rate
- related_document: GenericForeignKey, parent billing document
- related_item: ForeignKey, linked product entity
Methods:
- get_price(): Calculate line total before VAT
- get_price_inclusive(): Calculate line total with VAT
- get_raw_price(): Get base price before discounts
- __str__(): String representation
"""
class ServiceLine(CremeEntity):
"""
Service line item for billing documents.
Attributes:
- on_the_fly_item: str, custom service description
- comment: TextField, line item notes
- quantity: DecimalField, service quantity
- unit_price: DecimalField, price per unit
- unit: str, unit of measurement
- discount: DecimalField, line discount percentage
- discount_unit: int, discount type
- vat_value: ForeignKey, VAT rate
- related_document: GenericForeignKey, parent billing document
- related_item: ForeignKey, linked service entity
Methods:
- get_price(): Calculate line total before VAT
- get_price_inclusive(): Calculate line total with VAT
- get_raw_price(): Get base price before discounts
"""Status tracking for billing documents.
class InvoiceStatus(CremeModel):
"""
Status options for invoices.
Attributes:
- name: str, status name
- color: str, display color code
- order: int, display order
- is_default: bool, default status flag
Common Statuses:
- Draft, Pending, Sent, Paid, Overdue, Cancelled
"""
class QuoteStatus(CremeModel):
"""
Status options for quotes.
Attributes:
- name: str, status name
- color: str, display color code
- order: int, display order
- is_default: bool, default status flag
Common Statuses:
- Draft, Sent, Accepted, Rejected, Expired
"""
class SalesOrderStatus(CremeModel):
"""
Status options for sales orders.
Common Statuses:
- Pending, Confirmed, Processing, Shipped, Delivered, Cancelled
"""
class CreditNoteStatus(CremeModel):
"""
Status options for credit notes.
Common Statuses:
- Draft, Issued, Applied, Cancelled
"""Payment terms and conditions management.
class PaymentTerms(CremeModel):
"""
Payment terms templates for billing documents.
Attributes:
- name: str, terms name
- description: TextField, detailed terms text
- is_default: bool, default terms flag
Methods:
- __str__(): Return terms name
"""
class SettlementTerms(CremeModel):
"""
Settlement terms for payment conditions.
Attributes:
- name: str, settlement terms name
Examples:
- "Net 30", "Due on receipt", "2/10 Net 30"
"""from creme.billing import get_invoice_model
from creme.billing.models import InvoiceStatus, ProductLine
from datetime import date, timedelta
InvoiceModel = get_invoice_model()
# Create basic invoice
invoice = InvoiceModel.objects.create(
name="INV-2023-001",
issuing_date=date.today(),
expiration_date=date.today() + timedelta(days=30),
currency=default_currency,
status=InvoiceStatus.objects.get(name="Draft")
)
# Add product lines
ProductLine.objects.create(
related_document=invoice,
on_the_fly_item="Web Development Services",
quantity=40,
unit_price=75.00,
unit="hours",
vat_value=default_vat
)
ProductLine.objects.create(
related_document=invoice,
on_the_fly_item="Hosting Setup",
quantity=1,
unit_price=200.00,
unit="service",
discount=10.0, # 10% discount
vat_value=default_vat
)from creme.billing import get_quote_model
from creme.billing.models import QuoteStatus
QuoteModel = get_quote_model()
# Create quote
quote = QuoteModel.objects.create(
name="QUO-2023-001",
issuing_date=date.today(),
expiration_date=date.today() + timedelta(days=45),
status=QuoteStatus.objects.get(name="Draft")
)
# Convert accepted quote to invoice
if quote.status.name == "Accepted":
invoice = quote.convert_to_invoice()
invoice.save()# Quote -> Sales Order -> Invoice workflow
from creme.billing import get_salesorder_model
SalesOrderModel = get_salesorder_model()
# Step 1: Create and send quote
quote = QuoteModel.objects.create(
name="QUO-2023-002",
issuing_date=date.today()
)
quote.send_to_customer()
# Step 2: Convert accepted quote to sales order
if quote.status.name == "Accepted":
sales_order = quote.convert_to_sales_order()
sales_order.save()
# Step 3: Convert sales order to invoice when ready to bill
invoice = sales_order.convert_to_invoice()
invoice.save()
# Step 4: Generate PDF and send invoice
pdf_content = invoice.build_pdf()
invoice.send_email(attach_pdf=True)from creme.billing import get_creditnote_model
CreditNoteModel = get_creditnote_model()
# Create credit note for returned goods
credit_note = CreditNoteModel.objects.create(
name="CN-2023-001",
issuing_date=date.today(),
additional_info="Credit for returned merchandise"
)
# Add credit line
ServiceLine.objects.create(
related_document=credit_note,
on_the_fly_item="Product Return Credit",
quantity=1,
unit_price=-150.00, # Negative amount for credit
vat_value=default_vat
)
# Apply credit to original invoice
credit_note.apply_to_invoice(original_invoice)from creme.billing.models import Payment
# Record payment
payment = Payment.objects.create(
invoice=invoice,
amount=invoice.get_total(),
payment_date=date.today(),
payment_method="Bank Transfer",
reference="TXN123456"
)
# Update invoice status
if payment.amount >= invoice.get_total():
paid_status = InvoiceStatus.objects.get(name="Paid")
invoice.status = paid_status
invoice.save()from django.db.models import Sum, Q
from datetime import datetime
# Calculate monthly revenue
current_month = datetime.now().replace(day=1)
monthly_revenue = InvoiceModel.objects.filter(
issuing_date__gte=current_month,
status__name="Paid"
).aggregate(total=Sum('total_no_vat'))['total'] or 0
# Outstanding invoices
outstanding = InvoiceModel.objects.filter(
status__name__in=["Sent", "Overdue"]
).aggregate(total=Sum('total_no_vat'))['total'] or 0
# Pending quotes
pending_quotes = QuoteModel.objects.filter(
status__name="Sent",
expiration_date__gte=date.today()
).count()
# Generate aging report
from datetime import timedelta
today = date.today()
aging_buckets = {
'current': InvoiceModel.objects.filter(
status__name__in=["Sent"],
expiration_date__gte=today
).aggregate(Sum('total_no_vat')),
'1-30_days': InvoiceModel.objects.filter(
expiration_date__lt=today,
expiration_date__gte=today - timedelta(days=30)
).aggregate(Sum('total_no_vat')),
'31-60_days': InvoiceModel.objects.filter(
expiration_date__lt=today - timedelta(days=30),
expiration_date__gte=today - timedelta(days=60)
).aggregate(Sum('total_no_vat'))
}# Generate PDF invoice
pdf_buffer = invoice.build_pdf()
# Custom PDF generation with template
from creme.billing.utils import generate_pdf_invoice
pdf_content = generate_pdf_invoice(
invoice=invoice,
template='custom_invoice_template.html',
logo_path='/path/to/company_logo.png'
)
# Email invoice to customer
invoice.send_email(
recipient_list=['customer@example.com'],
subject=f"Invoice {invoice.name}",
message="Please find attached your invoice.",
attach_pdf=True
)The billing system integrates with:
Install with Tessl CLI
npx tessl i tessl/pypi-creme-crmdocs