Skip to main content

Invoice synchronization library between Loggro and Siigo platforms

Project description

Invoice Synchronizer

A library used to synchronize different business systems

๐Ÿ“‹ What is this project?

Invoice Synchronizer is a library designed to synchronize invoices and related data between two business systems. The library provides a flexible architecture that allows data to flow seamlessly from a source platform to a destination platform.

Current Implementation:

  • Source: Loggro (Point of sale system)
  • Destination: SIIGO (Electronic billing system)

Extensible Design: Thanks to its clean architecture implementation, other clients can be easily integrated. The synchronization flow remains the same regardless of the platforms involved - simply implement the PlatformConnector interface for your desired system and the library will handle the rest.

The project synchronizes:

  • โœ… Clients: Creates and updates client information
  • โœ… Products: Synchronizes product catalog
  • โœ… Invoices: Transfers invoices with complete details

Architecture

The project implements Clean Architecture with the following layers:

  • Domain: Business models, interfaces and domain rules
  • Application: Use cases and application logic
  • Infrastructure: Concrete implementations (API connectors)
  • Presentation: User interfaces (library, CLI, web)

๐Ÿš€ Installation

Prerequisites

  • Python 3.10
  • Poetry 1.8.3 (dependency manager)

Install the project

Option 1: From PyPI (Recommended for end users)

# Install from PyPI
pip install invoice-synchronizer

Option 2: From GitHub source (For development)

# Clone the repository
git clone https://github.com/jueshebe/invoice_synchronizer.git
cd invoice-synchronizer

# Install dependencies and current application
poetry install

# Activate virtual environment
poetry shell

โš™๏ธ Configuration

1. Environment Variables

Create a .env file in the project root with the following variables:

Loggro Variables

# Loggro credentials
PIRPOS_USERNAME=your_email@example.com
PIRPOS_PASSWORD=your_pirpos_password

# Loggro configuration
PIRPOS_BATCH_SIZE=200
PIRPOS_TIMEOUT=30

SIIGO Variables

# SIIGO credentials
SIIGO_USERNAME=your_siigo_username
SIIGO_ACCESS_KEY=your_siigo_access_key

2. SIIGO_CONFIGURATION.json File

Create this file in the project root and configure SIIGO specific parameters:

{
    "retentions": [19855],
    "credit_note_id": 13143,
    "seller_id": 709,
    "max_requests_per_minute": 90,
    "token_max_hours_time_alive": 10,
    "credit_note_forward_days": 60
}

Parameters:

  • retentions: IDs of configured retentions in SIIGO for the invoices
  • credit_note_id: Credit note document ID in SIIGO
  • seller_id: Seller ID in SIIGO (used to associate the invoices with a seller)
  • max_requests_per_minute: API request limit per minute for SIIGO
  • token_max_hours_time_alive: Token lifetime in hours
  • credit_note_forward_days: Days to extend the search window from the initial invoice date to find and associate related credit notes

3. SYSTEM_CONFIGURATION.JSON File

Create this file in the project root and configure mappings between the System, Loggro and Siigo:

{
    "payments": [
        {
            "loggro_id": "Efectivo",
            "system_id": "Efectivo",
            "siigo_id": 3025
        },
        {
            "loggro_id": "Tarjeta dรฉbito",
            "system_id": "Transferencia bancaria",
            "siigo_id": 3027
        }
    ],
    "taxes": [
        {
            "loggro_id": "IVA19",
            "system_id": "IVA 19%",
            "siigo_id": "7066",
            "value": 0.19
        },
        {
            "loggro_id": "I CONSUMO",
            "system_id": "I CONSUMO",
            "siigo_id": "7081",
            "value": 0.08
        }
    ],
    "prefixes": [
        {
            "system_id": "LL",
            "loggro_id": "LL",
            "siigo_id": 13136,
            "siigo_code": 1
        }
    ],
    "invoice_status": [
        {
            "loggro_id": "Pagada",
            "system_id": "PAID",
            "siigo_id": 1
        },
        {
            "loggro_id": "Anulada",
            "system_id": "ANULATED",
            "siigo_id": 3
        }
    ]
}

Sections:

  • payments: Payment methods mapping
  • taxes: Tax mapping with their values
  • prefixes: Invoice prefix mapping
  • invoice_status: Invoice status mapping

4. default_user.json File

Create this file in the project root and configure the default client for invoices without a specific client:

{
    "name": "Consumidor Final",
    "last_name": null,
    "email": "no-reply@loggro.com",
    "phone": "3102830171",
    "address": "calle 35#27-16",
    "document_number": 222222222222,
    "check_digit": null,
    "document_type": 13,
    "responsibilities": "R-99-PN",
    "city_detail": {
        "city_name": "Villavicencio",
        "city_state": "Meta",
        "city_code": "50001",
        "country_code": "Co",
        "state_code": "50"
    }
}

๐Ÿ“š Library Usage

Import and Initialize

from datetime import datetime
from invoice_synchronizer import InvoiceSynchronizer

# Create synchronizer instance
synchronizer = InvoiceSynchronizer()

Complete Synchronization

# Synchronize all data
def sync_everything():
    # 1. Synchronize products
    synchronizer.update_products()
    
    # 2. Synchronize clients
    synchronizer.update_clients()
    
    # 3. Synchronize invoices for a specific range
    start_date = datetime(2026, 1, 1)
    end_date = datetime(2026, 1, 31)
    synchronizer.update_invoices(start_date, end_date, iterations=5)

# Execute synchronization
sync_everything()

Time Range Synchronization

from datetime import datetime

# Define date range
start_date = datetime(2026, 1, 15)  # January 15, 2026
end_date = datetime(2026, 1, 20)    # January 20, 2026

# Synchronize only invoices from the range
synchronizer.update_invoices(start_date, end_date, iterations=3)

Specific Synchronization

# Products only
synchronizer.update_products()

# Clients only
synchronizer.update_clients()

# Invoices from a specific day only
specific_date = datetime(2026, 1, 25)
synchronizer.update_invoices(specific_date, specific_date, iterations=1)

Complete Example

#!/usr/bin/env python3
"""Example usage of the invoice synchronizer."""

from datetime import datetime, timedelta
from invoice_synchronizer import InvoiceSynchronizer

def main():
    """Main synchronization function."""
    try:
        # Initialize synchronizer
        print("Initializing synchronizer...")
        synchronizer = InvoiceSynchronizer()
        
        # Synchronize products and clients
        print("Synchronizing products...")
        synchronizer.update_products()
        
        print("Synchronizing clients...")
        synchronizer.update_clients()
        
        # Synchronize invoices from the last week
        end_date = datetime.now()
        start_date = end_date - timedelta(days=7)
        
        print(f"Synchronizing invoices from {start_date.date()} to {end_date.date()}")
        synchronizer.update_invoices(start_date, end_date, iterations=5)
        
        print("โœ… Synchronization completed successfully")
        
    except Exception as e:
        print(f"โŒ Error during synchronization: {e}")
        raise

if __name__ == "__main__":
    main()

๐Ÿ“ Logs

The system automatically generates logs in:

  • Console: Real-time output
  • File: ~/.config/pirpos2siigo/logs.txt

Logs include:

  • Progress information
  • Errors and exceptions
  • Synchronization details

๐Ÿšจ Error Handling

Automatic Error Files

When synchronization encounters errors, the system can generate error files:

# Example of full synchronization with error handling
from datetime import datetime

start_date = datetime(2026, 1, 1)
end_date = datetime(2026, 1, 31)

# This returns a InvoicesProcessReport object with failed invoices
error_invoices = synchronizer.update_invoices(start_date, end_date, iterations=5)

# Save errors to file for later processing
if error_invoices.error_missing_invoices or error_invoices.error_outdated_invoices:
    print("There were errors updating the following invoices:")
    with open("error_invoices.json", "w", encoding="utf-8") as error_file:
        error_file.write(error_invoices.model_dump_json(indent=4))
    
    print(f"โŒ {len(error_invoices.error_missing_invoices)} missing invoices")
    print(f"โŒ {len(error_invoices.error_outdated_invoices)} outdated invoices")
    print("๐Ÿ“ Error details saved to 'error_invoices.json'")
else:
    print("โœ… All invoices synchronized successfully!")

Simple Error Recovery

For a quick retry of failed invoices, use this simple approach:

import json
from invoice_synchronizer import InvoiceSynchronizer
from invoice_synchronizer.application import InvoicesProcessReport

# Load failed invoices and retry
synchronizer = InvoiceSynchronizer()

with open("error_invoices.json", "r", encoding="utf-8") as f:
    data = json.load(f)
    failed_invoices = InvoicesProcessReport(**data)

# Process only the failed invoices
result = synchronizer.update_specific_invoices(failed_invoices)
print(f"Remaining errors: {len(result.error_missing_invoices + result.error_outdated_invoices)}")

Error File Structure

The error file contains:

{
    "error_missing_invoices": [
        {
            "client": {...},
            "invoice_id": {...},
            "payments": [...],
            "order_items": [...],
            "total": 150000.0,
            "status": "PAID"
        }
    ],
    "error_outdated_invoices": [...],
    "finished_invoices": [...]
}

๐Ÿ› ๏ธ Development Commands

# Run tests
poetry run pytest

# Type checking with MyPy
poetry run mypy invoice_synchronizer/

# Lint with PyLint
poetry run pylint invoice_synchronizer/

# Format code
poetry run black invoice_synchronizer/

๐Ÿ“– Project Structure

invoice_synchronizer/
โ”œโ”€โ”€ domain/              # Business rules
โ”‚   โ”œโ”€โ”€ models/         # Entities (Invoice, Product, User)
โ”‚   โ”œโ”€โ”€ repositories/   # Interfaces
โ”‚   โ””โ”€โ”€ errors/         # Domain exceptions
โ”œโ”€โ”€ application/        # Use cases
โ”‚   โ””โ”€โ”€ use_cases/      # Application logic
โ”œโ”€โ”€ infrastructure/     # Concrete implementations
โ”‚   โ”œโ”€โ”€ repositories/   # Connectors (PirPOS, SIIGO)
โ”‚   โ””โ”€โ”€ config.py       # Configuration
โ””โ”€โ”€ presentation/       # User interfaces
    โ””โ”€โ”€ lib/           # Library API

๐Ÿค Contributing

  1. Fork the project
  2. Create a feature branch (git checkout -b feature/AmazingFeature)
  3. Commit your changes (git commit -m 'Add some AmazingFeature')
  4. Push to the branch (git push origin feature/AmazingFeature)
  5. Open a Pull Request

๐Ÿ“„ License

This project is under the MIT License. See the LICENSE file for more details.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

invoice_synchronizer-3.1.0.dev5.tar.gz (31.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

invoice_synchronizer-3.1.0.dev5-py3-none-any.whl (38.0 kB view details)

Uploaded Python 3

File details

Details for the file invoice_synchronizer-3.1.0.dev5.tar.gz.

File metadata

  • Download URL: invoice_synchronizer-3.1.0.dev5.tar.gz
  • Upload date:
  • Size: 31.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.8.3 CPython/3.10.17 Darwin/24.5.0

File hashes

Hashes for invoice_synchronizer-3.1.0.dev5.tar.gz
Algorithm Hash digest
SHA256 df95d8916adcc7b7e1b1e20fa683df9fbc6040a42330ac84156469f08313ea83
MD5 8e22a5d8e2e209c9299643d241d93180
BLAKE2b-256 abc3609f8ae6cdc2b2cbedb5237a3d05e12c6e0e814d14d3b1435702e3b5e2fe

See more details on using hashes here.

File details

Details for the file invoice_synchronizer-3.1.0.dev5-py3-none-any.whl.

File metadata

File hashes

Hashes for invoice_synchronizer-3.1.0.dev5-py3-none-any.whl
Algorithm Hash digest
SHA256 df9b7a28d99741f41fa2339f6e7d930fdbf12c2e3b836f0309848ad827d8aac3
MD5 d3dba4bc29fd27c2820b05df47983908
BLAKE2b-256 2114776503b910adc17e9dd68a1f17e0739ddbf7ca6dae68caf724189d4b4db3

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page