Overview
TheSaleService class handles the complete lifecycle of sales transactions in the ERP system. It orchestrates inventory movements, journal entries, receivables, invoicing, and NCF (fiscal control number) generation in a single atomic transaction.
Namespace: App\Services\Sales\SalesServices\SaleService
Location: app/Services/Sales/SalesServices/SaleService.php
Dependencies
The service is constructed with the following dependencies injected via Laravel’s dependency injection:InventoryMovementService- Manages inventory stock movementsJournalEntryService- Creates accounting journal entriesReceivableService- Handles accounts receivable for credit salesInvoiceService- Generates invoices from salesNcfGeneratorInterface- Generates fiscal control numbers (NCF)
Methods
create()
Creates a new sale transaction with all related entities (items, inventory movements, accounting entries, and receivables).Sale data array with the following structure:
Returns the created Sale model with all relationships loaded
- Document Number Generation - Generates next sequential sale number from DocumentType
- Sale Record Creation - Creates the sale record with status
'completed' - NCF Generation - If
ncf_type_idprovided, generates fiscal control number - Sale Items Creation - Creates all sale item records
- Inventory Movements - Registers output movements for each product
- Accounting Treatment:
- Cash Sales: Creates immediate journal entry (Debit: Cash Account, Credit: Income Account 4.1)
- Credit Sales: Creates receivable record with 30-day due date
- Invoice Generation - Creates corresponding invoice record
Example Usage
cancel()
Cancels an existing sale and reverses all related accounting, inventory, and receivable transactions.The Sale model instance to cancel
Cancellation reason for audit trail. Defaults to “Anulación de venta manual”
Returns
true if cancellation was successful- Sale must not already be cancelled
- For credit sales, the receivable must not have any payments applied
- If receivable has partial payments, throws exception: “No se puede anular: El cliente ya tiene abonos.”
- Receivable Cancellation - For credit sales, cancels the receivable if no payments exist
- NCF Status Update - Updates NCF log status to
'voided'with cancellation reason - Accounting Reversal - Creates reversal journal entry:
- Debit: Income Account (4.1)
- Credit: Cash/Receivable Account (depending on original payment type)
- Inventory Reversal - Creates adjustment movements to restore stock
- Invoice Cancellation - Marks related invoice as cancelled
- Sale Status Update - Sets sale status to
'canceled'
Example Usage
Protected Methods
These methods are used internally by the service and are not meant to be called directly.generateSaleAccountingEntry()
Generates the accounting journal entry for cash sales.- Debits the payment method’s accounting account (or default 1.1.01 Cash)
- Credits Income Account 4.1 (Sales Revenue)
- Description format: “Venta Contado () - ”
- Status:
'posted'(immediately posted)
SaleService.php:147-169
generateCancellationAccountingEntry()
Generates the reversal accounting journal entry when cancelling a sale.- Debits Income Account 4.1 (reverses revenue recognition)
- Credits:
- Cash Account (1.1.01) for cash sales
- Client’s accounting account or default Receivable Account (1.1.02) for credit sales
- Reference format: “REV-”
- Description: “Anulación Venta ”
SaleService.php:171-191
Sale Model Constants
Payment Types
Status Constants
Exception Handling
The service may throw exceptions in the following scenarios:Configuration Errors
Configuration Errors
Exception:
"Configuración contable incompleta."Cause: Missing required accounting accounts (Income Account 4.1 or Cash Account 1.1.01)Resolution: Ensure all required accounting accounts are configured in the systemAlready Cancelled
Already Cancelled
Exception:
"La venta ya se encuentra anulada."Cause: Attempting to cancel a sale that is already cancelledResolution: Check sale status before attempting cancellationReceivable Has Payments
Receivable Has Payments
Exception:
"No se puede anular: El cliente ya tiene abonos."Cause: Attempting to cancel a credit sale that has received partial or full paymentResolution: Payments must be reversed before sale cancellation, or use a different business process for handling paid invoicesInsufficient Stock
Insufficient Stock
Cause: Product quantity exceeds available warehouse stock during sale creationResolution: Thrown by InventoryMovementService - verify stock availability before processing sale
Integration Points
InventoryMovementService
- On Sale Creation: Registers
TYPE_OUTPUTmovements for each product - On Sale Cancellation: Registers
TYPE_ADJUSTMENTmovements to restore inventory - See InventoryMovementService for details
JournalEntryService
- Cash Sales: Creates posted journal entries for immediate revenue recognition
- Sale Cancellation: Creates reversal entries to negate original transaction
- See JournalEntryService for details
ReceivableService
- Credit Sales: Creates receivable records with 30-day due date
- Cancellation: Cancels receivables if no payments have been applied
- See ReceivableService for details
InvoiceService
- Creates invoice record from completed sale
- Cancels invoice when sale is cancelled
NcfGeneratorInterface
- Generates fiscal control numbers (NCF) when
ncf_type_idis provided - Updates NCF log status on cancellation
Database Transaction Safety
Bothcreate() and cancel() methods wrap all operations in database transactions using DB::transaction(). If any operation fails, all changes are rolled back automatically, ensuring data consistency.
Related Models
- Sale Model - Main sale entity
- SaleItem Model - Individual line items
- Receivable Model - Accounts receivable
- JournalEntry Model - Accounting entries
- InventoryMovement Model - Inventory tracking