473 lines
14 KiB
PHP
473 lines
14 KiB
PHP
<?php
|
|
namespace Domain\Finances;
|
|
|
|
class FakturowniaImportRepository
|
|
{
|
|
private $mdb;
|
|
private $tablesReady = false;
|
|
|
|
public function __construct( $mdb = null )
|
|
{
|
|
if ( $mdb )
|
|
$this -> mdb = $mdb;
|
|
else
|
|
{
|
|
global $mdb;
|
|
$this -> mdb = $mdb;
|
|
}
|
|
}
|
|
|
|
public function ensureTables()
|
|
{
|
|
if ( $this -> tablesReady )
|
|
return;
|
|
|
|
$this -> mdb -> query(
|
|
'CREATE TABLE IF NOT EXISTS `fakturownia_client_mappings` (
|
|
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
|
`external_client_key` VARCHAR(191) NOT NULL,
|
|
`external_name` VARCHAR(255) NOT NULL,
|
|
`crm_client_id` INT UNSIGNED NOT NULL,
|
|
`created_at` DATETIME NOT NULL,
|
|
`updated_at` DATETIME NOT NULL,
|
|
PRIMARY KEY (`id`),
|
|
UNIQUE KEY `uniq_external_client_key` (`external_client_key`)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8'
|
|
);
|
|
|
|
$this -> mdb -> query(
|
|
'CREATE TABLE IF NOT EXISTS `fakturownia_item_mappings` (
|
|
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
|
`external_item_key` VARCHAR(191) NOT NULL,
|
|
`external_name` VARCHAR(255) NOT NULL,
|
|
`finance_category_id` INT UNSIGNED NOT NULL,
|
|
`created_at` DATETIME NOT NULL,
|
|
`updated_at` DATETIME NOT NULL,
|
|
PRIMARY KEY (`id`),
|
|
UNIQUE KEY `uniq_external_item_key` (`external_item_key`)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8'
|
|
);
|
|
|
|
$this -> mdb -> query(
|
|
'CREATE TABLE IF NOT EXISTS `fakturownia_skipped_positions` (
|
|
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
|
`external_id` VARCHAR(64) NOT NULL,
|
|
`document_type` VARCHAR(32) NOT NULL,
|
|
`external_item_key` VARCHAR(191) NOT NULL,
|
|
`item_name` VARCHAR(255) NOT NULL,
|
|
`created_at` DATETIME NOT NULL,
|
|
PRIMARY KEY (`id`),
|
|
UNIQUE KEY `uniq_doc_item` (`external_id`, `document_type`, `external_item_key`)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8'
|
|
);
|
|
|
|
$this -> mdb -> query(
|
|
'CREATE TABLE IF NOT EXISTS `fakturownia_imported_documents` (
|
|
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
|
`external_document_key` VARCHAR(191) NOT NULL,
|
|
`document_type` VARCHAR(32) NOT NULL,
|
|
`external_id` VARCHAR(64) NOT NULL,
|
|
`source_date` DATE NULL,
|
|
`amount` DECIMAL(12,2) NOT NULL DEFAULT 0.00,
|
|
`finance_operation_ids` TEXT NULL,
|
|
`meta_json` LONGTEXT NULL,
|
|
`imported_at` DATETIME NOT NULL,
|
|
PRIMARY KEY (`id`),
|
|
UNIQUE KEY `uniq_external_document_key` (`external_document_key`)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8'
|
|
);
|
|
|
|
$this -> mdb -> query(
|
|
'CREATE TABLE IF NOT EXISTS `fakturownia_unmapped_queue` (
|
|
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
|
`queue_type` VARCHAR(32) NOT NULL,
|
|
`external_key` VARCHAR(191) NOT NULL,
|
|
`external_name` VARCHAR(255) NOT NULL,
|
|
`payload_json` LONGTEXT NULL,
|
|
`hits` INT UNSIGNED NOT NULL DEFAULT 1,
|
|
`resolved` TINYINT(1) NOT NULL DEFAULT 0,
|
|
`first_seen_at` DATETIME NOT NULL,
|
|
`last_seen_at` DATETIME NOT NULL,
|
|
PRIMARY KEY (`id`),
|
|
UNIQUE KEY `uniq_type_key` (`queue_type`, `external_key`)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8'
|
|
);
|
|
|
|
$this -> mdb -> query(
|
|
'CREATE TABLE IF NOT EXISTS `fakturownia_import_state` (
|
|
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
|
`state_key` VARCHAR(191) NOT NULL,
|
|
`state_value` LONGTEXT NULL,
|
|
`updated_at` DATETIME NOT NULL,
|
|
PRIMARY KEY (`id`),
|
|
UNIQUE KEY `uniq_state_key` (`state_key`)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8'
|
|
);
|
|
|
|
$this -> tablesReady = true;
|
|
}
|
|
|
|
public function getClientMapping( $externalClientKey )
|
|
{
|
|
$this -> ensureTables();
|
|
return $this -> mdb -> get( 'fakturownia_client_mappings', '*', [
|
|
'external_client_key' => $externalClientKey
|
|
] );
|
|
}
|
|
|
|
public function saveClientMapping( $externalClientKey, $externalName, $crmClientId )
|
|
{
|
|
$this -> ensureTables();
|
|
|
|
$current = $this -> getClientMapping( $externalClientKey );
|
|
$now = date( 'Y-m-d H:i:s' );
|
|
|
|
if ( $current )
|
|
{
|
|
$this -> mdb -> update( 'fakturownia_client_mappings', [
|
|
'external_name' => $externalName,
|
|
'crm_client_id' => (int)$crmClientId,
|
|
'updated_at' => $now
|
|
], [ 'id' => (int)$current['id'] ] );
|
|
}
|
|
else
|
|
{
|
|
$this -> mdb -> insert( 'fakturownia_client_mappings', [
|
|
'external_client_key' => $externalClientKey,
|
|
'external_name' => $externalName,
|
|
'crm_client_id' => (int)$crmClientId,
|
|
'created_at' => $now,
|
|
'updated_at' => $now
|
|
] );
|
|
}
|
|
|
|
$this -> resolveQueueItem( 'client', $externalClientKey );
|
|
}
|
|
|
|
public function getItemMapping( $externalItemKey )
|
|
{
|
|
$this -> ensureTables();
|
|
return $this -> mdb -> get( 'fakturownia_item_mappings', '*', [
|
|
'external_item_key' => $externalItemKey
|
|
] );
|
|
}
|
|
|
|
public function saveItemMapping( $externalItemKey, $externalName, $financeCategoryId )
|
|
{
|
|
$this -> ensureTables();
|
|
|
|
$current = $this -> getItemMapping( $externalItemKey );
|
|
$now = date( 'Y-m-d H:i:s' );
|
|
|
|
if ( $current )
|
|
{
|
|
$this -> mdb -> update( 'fakturownia_item_mappings', [
|
|
'external_name' => $externalName,
|
|
'finance_category_id' => (int)$financeCategoryId,
|
|
'updated_at' => $now
|
|
], [ 'id' => (int)$current['id'] ] );
|
|
}
|
|
else
|
|
{
|
|
$this -> mdb -> insert( 'fakturownia_item_mappings', [
|
|
'external_item_key' => $externalItemKey,
|
|
'external_name' => $externalName,
|
|
'finance_category_id' => (int)$financeCategoryId,
|
|
'created_at' => $now,
|
|
'updated_at' => $now
|
|
] );
|
|
}
|
|
|
|
$this -> resolveQueueItem( 'item', $externalItemKey );
|
|
}
|
|
|
|
public function markDocumentPositionSkipped( $externalId, $documentType, $externalItemKey, $itemName )
|
|
{
|
|
$this -> ensureTables();
|
|
|
|
$existing = $this -> mdb -> get( 'fakturownia_skipped_positions', 'id', [
|
|
'AND' => [
|
|
'external_id' => (string)$externalId,
|
|
'document_type' => (string)$documentType,
|
|
'external_item_key' => (string)$externalItemKey
|
|
]
|
|
] );
|
|
if ( $existing )
|
|
return;
|
|
|
|
$this -> mdb -> insert( 'fakturownia_skipped_positions', [
|
|
'external_id' => (string)$externalId,
|
|
'document_type' => (string)$documentType,
|
|
'external_item_key' => (string)$externalItemKey,
|
|
'item_name' => (string)$itemName,
|
|
'created_at' => date( 'Y-m-d H:i:s' )
|
|
] );
|
|
}
|
|
|
|
public function isDocumentPositionSkipped( $externalId, $documentType, $externalItemKey )
|
|
{
|
|
$this -> ensureTables();
|
|
return (bool)$this -> mdb -> has( 'fakturownia_skipped_positions', [
|
|
'AND' => [
|
|
'external_id' => (string)$externalId,
|
|
'document_type' => (string)$documentType,
|
|
'external_item_key' => (string)$externalItemKey
|
|
]
|
|
] );
|
|
}
|
|
|
|
public function removeOccurrenceFromItemQueue( $externalItemKey, $externalId )
|
|
{
|
|
$this -> ensureTables();
|
|
|
|
$row = $this -> mdb -> get( 'fakturownia_unmapped_queue', '*', [
|
|
'AND' => [
|
|
'queue_type' => 'item',
|
|
'external_key' => (string)$externalItemKey
|
|
]
|
|
] );
|
|
if ( !$row )
|
|
return;
|
|
|
|
$payload = [];
|
|
if ( isset( $row['payload_json'] ) && $row['payload_json'] )
|
|
{
|
|
$decoded = json_decode( $row['payload_json'], true );
|
|
if ( is_array( $decoded ) )
|
|
$payload = $decoded;
|
|
}
|
|
|
|
$occurrences = [];
|
|
if ( isset( $payload['occurrences'] ) && is_array( $payload['occurrences'] ) )
|
|
{
|
|
foreach ( $payload['occurrences'] as $occ )
|
|
{
|
|
if ( !is_array( $occ ) )
|
|
continue;
|
|
if ( (string)( $occ['document_id'] ?? '' ) === (string)$externalId )
|
|
continue;
|
|
$occurrences[] = $occ;
|
|
}
|
|
}
|
|
|
|
if ( empty( $occurrences ) )
|
|
{
|
|
$this -> mdb -> delete( 'fakturownia_unmapped_queue', [ 'id' => (int)$row['id'] ] );
|
|
return;
|
|
}
|
|
|
|
$payload['occurrences'] = $occurrences;
|
|
$this -> mdb -> update( 'fakturownia_unmapped_queue', [
|
|
'payload_json' => json_encode( $payload, JSON_UNESCAPED_UNICODE ),
|
|
'hits' => count( $occurrences ),
|
|
'last_seen_at' => date( 'Y-m-d H:i:s' )
|
|
], [ 'id' => (int)$row['id'] ] );
|
|
}
|
|
|
|
public function isDocumentImported( $externalDocumentKey )
|
|
{
|
|
$this -> ensureTables();
|
|
return (bool)$this -> mdb -> has( 'fakturownia_imported_documents', [
|
|
'external_document_key' => $externalDocumentKey
|
|
] );
|
|
}
|
|
|
|
public function markDocumentImported( $externalDocumentKey, $documentType, $externalId, $sourceDate, $amount, $operationIds, $meta )
|
|
{
|
|
$this -> ensureTables();
|
|
|
|
$now = date( 'Y-m-d H:i:s' );
|
|
$data = [
|
|
'external_document_key' => $externalDocumentKey,
|
|
'document_type' => $documentType,
|
|
'external_id' => (string)$externalId,
|
|
'source_date' => $sourceDate,
|
|
'amount' => (float)$amount,
|
|
'finance_operation_ids' => implode( ',', $operationIds ),
|
|
'meta_json' => json_encode( $meta, JSON_UNESCAPED_UNICODE ),
|
|
'imported_at' => $now
|
|
];
|
|
|
|
$existing = $this -> mdb -> get( 'fakturownia_imported_documents', 'id', [
|
|
'external_document_key' => $externalDocumentKey
|
|
] );
|
|
|
|
if ( $existing )
|
|
{
|
|
$this -> mdb -> update( 'fakturownia_imported_documents', $data, [
|
|
'id' => (int)$existing
|
|
] );
|
|
return;
|
|
}
|
|
|
|
$this -> mdb -> insert( 'fakturownia_imported_documents', $data );
|
|
}
|
|
|
|
public function queueUnmapped( $queueType, $externalKey, $externalName, $payload )
|
|
{
|
|
$this -> ensureTables();
|
|
|
|
$existing = $this -> mdb -> get( 'fakturownia_unmapped_queue', '*', [
|
|
'AND' => [
|
|
'queue_type' => $queueType,
|
|
'external_key' => $externalKey
|
|
]
|
|
] );
|
|
|
|
$now = date( 'Y-m-d H:i:s' );
|
|
|
|
// Dla queue_type='item' agregujemy occurrences per (document_id, item_key).
|
|
// Kazde wystapienie pozycji na innej fakturze to osobny wpis w payload.occurrences,
|
|
// zeby UI mogl pokazac liste i pozwolic na pomijanie per konkretna faktura.
|
|
if ( $queueType === 'item' )
|
|
{
|
|
$occurrence = $payload;
|
|
$payloadToStore = [ 'occurrences' => [ $occurrence ] ];
|
|
|
|
if ( $existing )
|
|
{
|
|
$existingPayload = [];
|
|
if ( isset( $existing['payload_json'] ) && $existing['payload_json'] )
|
|
{
|
|
$decoded = json_decode( $existing['payload_json'], true );
|
|
if ( is_array( $decoded ) )
|
|
$existingPayload = $decoded;
|
|
}
|
|
|
|
$occurrences = ( isset( $existingPayload['occurrences'] ) && is_array( $existingPayload['occurrences'] ) )
|
|
? $existingPayload['occurrences']
|
|
: [];
|
|
|
|
// Dedup po document_id — powtorny import nadpisuje istniejace wystapienie.
|
|
$occurrences = array_values( array_filter( $occurrences, function( $occ ) use ( $occurrence )
|
|
{
|
|
if ( !is_array( $occ ) )
|
|
return false;
|
|
return (string)( $occ['document_id'] ?? '' ) !== (string)( $occurrence['document_id'] ?? '' );
|
|
} ) );
|
|
|
|
$occurrences[] = $occurrence;
|
|
$payloadToStore = [ 'occurrences' => $occurrences ];
|
|
|
|
$this -> mdb -> update( 'fakturownia_unmapped_queue', [
|
|
'external_name' => $externalName,
|
|
'payload_json' => json_encode( $payloadToStore, JSON_UNESCAPED_UNICODE ),
|
|
'hits' => count( $occurrences ),
|
|
'resolved' => 0,
|
|
'last_seen_at' => $now
|
|
], [ 'id' => (int)$existing['id'] ] );
|
|
return;
|
|
}
|
|
|
|
$this -> mdb -> insert( 'fakturownia_unmapped_queue', [
|
|
'queue_type' => $queueType,
|
|
'external_key' => $externalKey,
|
|
'external_name' => $externalName,
|
|
'payload_json' => json_encode( $payloadToStore, JSON_UNESCAPED_UNICODE ),
|
|
'hits' => 1,
|
|
'resolved' => 0,
|
|
'first_seen_at' => $now,
|
|
'last_seen_at' => $now
|
|
] );
|
|
return;
|
|
}
|
|
|
|
// Pozostale queue_type (client) — zachowanie bez zmian.
|
|
$payloadJson = json_encode( $payload, JSON_UNESCAPED_UNICODE );
|
|
|
|
if ( $existing )
|
|
{
|
|
$this -> mdb -> update( 'fakturownia_unmapped_queue', [
|
|
'external_name' => $externalName,
|
|
'payload_json' => $payloadJson,
|
|
'hits[+]' => 1,
|
|
'resolved' => 0,
|
|
'last_seen_at' => $now
|
|
], [ 'id' => (int)$existing['id'] ] );
|
|
return;
|
|
}
|
|
|
|
$this -> mdb -> insert( 'fakturownia_unmapped_queue', [
|
|
'queue_type' => $queueType,
|
|
'external_key' => $externalKey,
|
|
'external_name' => $externalName,
|
|
'payload_json' => $payloadJson,
|
|
'hits' => 1,
|
|
'resolved' => 0,
|
|
'first_seen_at' => $now,
|
|
'last_seen_at' => $now
|
|
] );
|
|
}
|
|
|
|
public function pendingClientMappings()
|
|
{
|
|
$this -> ensureTables();
|
|
return $this -> mdb -> select( 'fakturownia_unmapped_queue', '*', [
|
|
'AND' => [
|
|
'queue_type' => 'client',
|
|
'resolved' => 0
|
|
],
|
|
'ORDER' => [ 'last_seen_at' => 'DESC' ]
|
|
] );
|
|
}
|
|
|
|
public function pendingItemMappings()
|
|
{
|
|
$this -> ensureTables();
|
|
return $this -> mdb -> select( 'fakturownia_unmapped_queue', '*', [
|
|
'AND' => [
|
|
'queue_type' => 'item',
|
|
'resolved' => 0,
|
|
'external_key[!]' => 'name:faktura bez pozycji'
|
|
],
|
|
'ORDER' => [ 'last_seen_at' => 'DESC' ]
|
|
] );
|
|
}
|
|
|
|
public function saveState( $stateKey, $stateValue )
|
|
{
|
|
$this -> ensureTables();
|
|
|
|
$existing = $this -> mdb -> get( 'fakturownia_import_state', '*', [
|
|
'state_key' => $stateKey
|
|
] );
|
|
$now = date( 'Y-m-d H:i:s' );
|
|
|
|
if ( $existing )
|
|
{
|
|
$this -> mdb -> update( 'fakturownia_import_state', [
|
|
'state_value' => $stateValue,
|
|
'updated_at' => $now
|
|
], [ 'id' => (int)$existing['id'] ] );
|
|
return;
|
|
}
|
|
|
|
$this -> mdb -> insert( 'fakturownia_import_state', [
|
|
'state_key' => $stateKey,
|
|
'state_value' => $stateValue,
|
|
'updated_at' => $now
|
|
] );
|
|
}
|
|
|
|
public function getState( $stateKey )
|
|
{
|
|
$this -> ensureTables();
|
|
return $this -> mdb -> get( 'fakturownia_import_state', 'state_value', [
|
|
'state_key' => $stateKey
|
|
] );
|
|
}
|
|
|
|
private function resolveQueueItem( $queueType, $externalKey )
|
|
{
|
|
$this -> mdb -> update( 'fakturownia_unmapped_queue', [
|
|
'resolved' => 1,
|
|
'last_seen_at' => date( 'Y-m-d H:i:s' )
|
|
], [
|
|
'AND' => [
|
|
'queue_type' => $queueType,
|
|
'external_key' => $externalKey
|
|
]
|
|
] );
|
|
}
|
|
}
|