Skip to main content

Overview

The Sales module is the core of the ERP system, managing all sales transactions from creation to completion. It supports both cash and credit sales, integrates with inventory for automatic stock adjustments, and generates accounting entries automatically.

Point of Sale

Fast checkout with multiple payment methods

NCF Generation

Tax-compliant invoice numbering

Accounting

Automatic journal entries

Key Features

Sale Types

The system supports two primary sale types:
Cash sales are completed immediately with payment received at the time of transaction.Features:
  • Multiple payment methods (cash, card, transfer)
  • Cash handling with change calculation
  • Immediate accounting entry generation
  • Automatic invoice generation
  • Real-time inventory adjustment
app/Models/Sales/Sale.php
const PAYMENT_CASH   = 'cash';
const PAYMENT_CREDIT = 'credit';

public static function getPaymentTypes(): array
{
    return [
        self::PAYMENT_CASH   => 'Contado',
        self::PAYMENT_CREDIT => 'Crédito',
    ];
}

Sale Workflow

1

Create Sale

Start a new sale by selecting the client, warehouse, and payment type.
$sale = Sale::create([
    'client_id'    => $clientId,
    'warehouse_id' => $warehouseId,
    'payment_type' => Sale::PAYMENT_CASH,
    'status'       => Sale::STATUS_COMPLETED,
]);
2

Add Items

Add products to the sale with quantities and prices.
foreach ($data['items'] as $item) {
    $sale->items()->create([
        'product_id' => $item['product_id'],
        'quantity'   => $item['quantity'],
        'unit_price' => $item['price'],
        'subtotal'   => $item['quantity'] * $item['price'],
    ]);
}
3

Adjust Inventory

Automatically reduce stock for each product sold.
$this->inventoryService->register([
    'warehouse_id'   => $data['warehouse_id'],
    'product_id'     => $item['product_id'],
    'quantity'       => $item['quantity'],
    'type'           => InventoryMovement::TYPE_OUTPUT,
    'description'    => "Venta {$saleNumber}",
    'reference_type' => Sale::class,
    'reference_id'   => $sale->id,
]);
4

Generate Accounting

Create journal entries for the sale based on payment type.Cash Sale:
Debit:  Cash/Bank Account (1.1.01)     $1,000
Credit: Sales Revenue (4.1)            $1,000
Credit Sale:
Debit:  Accounts Receivable (1.1.02)   $1,000
Credit: Sales Revenue (4.1)            $1,000
5

Generate Invoice

Automatically create and print the invoice with NCF if applicable.
$this->invoiceService->createFromSale($sale);

Sale Status Management

Sales can have the following statuses:
app/Models/Sales/Sale.php
const STATUS_COMPLETED = 'completed';
const STATUS_CANCELED  = 'canceled';

public static function getStatuses(): array
{
    return [
        self::STATUS_COMPLETED => 'Completada',
        self::STATUS_CANCELED  => 'Anulada',
    ];
}

Canceling Sales

When a sale is canceled, the system:
  1. Reverses inventory - Returns products to stock
  2. Reverses accounting - Creates reversal journal entry
  3. Cancels receivable - If credit sale with no payments
  4. Voids NCF - Updates NCF status for DGII compliance
  5. Cancels invoice - Marks invoice as cancelled
app/Services/Sales/SalesServices/SaleService.php
public function cancel(Sale $sale, ?string $reason = null): bool
{
    return DB::transaction(function () use ($sale, $reason) {
        // Reverse inventory
        foreach ($sale->items as $item) {
            $this->inventoryService->register([
                'warehouse_id'   => $sale->warehouse_id,
                'product_id'     => $item->product_id,
                'quantity'       => $item->quantity,
                'type'           => InventoryMovement::TYPE_ADJUSTMENT,
                'description'    => "Reversión {$sale->number}",
            ]);
        }

        // Reverse accounting
        $this->generateCancellationAccountingEntry($sale);

        // Update NCF status
        NcfLog::where('sale_id', $sale->id)
            ->update([
                'status' => NcfLog::STATUS_VOIDED,
                'cancellation_reason' => $reason
            ]);

        return $sale->update(['status' => Sale::STATUS_CANCELED]);
    });
}
Credit sales cannot be canceled if any payments have been applied. The receivable must have a zero balance or be fully unpaid.

Data Structure

Sale Model

app/Models/Sales/Sale.php
protected $fillable = [
    'document_type_id',
    'number',
    'client_id',
    'warehouse_id',
    'user_id',
    'sale_date',
    'total_amount',
    'payment_type',
    'tipo_pago_id',
    'cash_received',
    'cash_change',
    'status',
    'notes',
];

Relationships

public function items(): HasMany {
    return $this->hasMany(SaleItem::class);
}
Contains the products, quantities, and prices for each line item.
public function client(): BelongsTo {
    return $this->belongsTo(Client::class);
}
The customer who made the purchase.
public function warehouse(): BelongsTo {
    return $this->belongsTo(Warehouse::class);
}
The warehouse from which products were sold.
public function invoice(): HasOne {
    return $this->hasOne(Invoice::class);
}
The generated invoice document.
public function ncfLog(): HasOne {
    return $this->hasOne(NcfLog::class, 'sale_id');
}
The fiscal number assignment record.

Reporting & Exports

The Sales module includes powerful reporting capabilities:

Filters

Filter sales by:
  • Date range
  • Client
  • Payment type (cash/credit)
  • Status (completed/canceled)
  • Amount range
  • Search (number, client name, notes)
app/Filters/Sales/SalesFilters/SaleFilters.php
$sales = (new SaleFilters($request))
    ->apply(Sale::query()->withIndexRelations())
    ->latest()
    ->paginate();

Excel Export

Export filtered sales data with:
  • Sale number and date
  • Client information
  • Payment type and amount
  • Cash received and change
  • Status
  • User who created the sale
app/Http/Controllers/Sales/SaleController.php
public function export(Request $request)
{
    $query = (new SaleFilters($request))
        ->apply(Sale::query());

    $fileName = 'reporte-ventas-' . now()->format('d-m-Y-H-i') . '.xlsx';

    return Excel::download(new SalesExport($query), $fileName);
}

Point of Sale

Learn about the POS interface

NCF Generation

Understand tax compliance

Inventory

Stock management integration

Accounting

Financial integration

Build docs developers (and LLMs) love