This commit is contained in:
2026-03-27 00:08:34 +01:00
parent 51ea2030e4
commit 3f072c5906
13 changed files with 232 additions and 101 deletions

3
.env
View File

@@ -16,3 +16,6 @@ DB_CHARSET=utf8mb4
INTEGRATIONS_SECRET=nB3sTkXAbBLqA2Ent74R9Mi1118bAbWa
pracownia.key=9554daf4bbcbbb5e72a2b48ee7d6a7f20262713d72484b781460e2c772d813fc
login=jacek.pyziak@project-pro.pl
password=ProjectPro2025!

View File

@@ -0,0 +1 @@
[ 508ms] [ERROR] Failed to load resource: the server responded with a status of 404 () @ https://orderpro.projectpro.pl/favicon.ico:0

View File

@@ -380,8 +380,8 @@
},
"cron.php": {
"type": "-",
"size": 4212,
"lmtime": 1772997988131,
"size": 796,
"lmtime": 1774474838216,
"modified": false
},
"debug_allegro_offer_image.php": {
@@ -965,8 +965,8 @@
"DOCS": {
"ARCHITECTURE.md": {
"type": "-",
"size": 33394,
"lmtime": 1774285995231,
"size": 35120,
"lmtime": 1774475884811,
"modified": false
},
"DB_SCHEMA.md": {
@@ -995,14 +995,14 @@
},
"TECH_CHANGELOG.md": {
"type": "-",
"size": 51132,
"lmtime": 1774286010490,
"size": 57304,
"lmtime": 1774475891183,
"modified": false
},
"todo.md": {
"type": "-",
"size": 3510,
"lmtime": 1774296458106,
"size": 3512,
"lmtime": 1774474971584,
"modified": false
}
},
@@ -2250,8 +2250,8 @@
"css": {
"app.css": {
"type": "-",
"size": 53388,
"lmtime": 1774304255719,
"size": 43993,
"lmtime": 1774474931663,
"modified": false
},
"app.css.map": {
@@ -2262,8 +2262,8 @@
},
"login.css": {
"type": "-",
"size": 5308,
"lmtime": 1773008638575,
"size": 5996,
"lmtime": 1774474932148,
"modified": false
},
"login.css.map": {
@@ -2284,17 +2284,17 @@
"img": {},
"js": {
"modules": {
"automation-form.js": {
"type": "-",
"size": 7251,
"lmtime": 1774475530521,
"modified": false
},
"jquery-alerts.js": {
"type": "-",
"size": 5768,
"lmtime": 1771873304132,
"modified": false
},
"automation-form.js": {
"type": "-",
"size": 5053,
"lmtime": 1773789606719,
"modified": false
}
}
}
@@ -2382,6 +2382,12 @@
"lmtime": 1773789611848,
"modified": false
},
"_delivery-status-mappings.scss": {
"type": "-",
"size": 212,
"lmtime": 1774304202952,
"modified": false
},
"_delivery-status.scss": {
"type": "-",
"size": 802,
@@ -2396,8 +2402,8 @@
},
"_printing.scss": {
"type": "-",
"size": 939,
"lmtime": 0,
"size": 1115,
"lmtime": 1774474924746,
"modified": false
},
"_shipment-presets.scss": {
@@ -2405,12 +2411,6 @@
"size": 2629,
"lmtime": 1774219643850,
"modified": false
},
"_delivery-status-mappings.scss": {
"type": "-",
"size": 212,
"lmtime": 1774304202952,
"modified": false
}
},
"shared": {
@@ -2491,9 +2491,9 @@
},
"list.php": {
"type": "-",
"size": 3380,
"lmtime": 1773004094862,
"modified": true
"size": 1603,
"lmtime": 1774473665048,
"modified": false
},
"partials": {
"email-send-modal.php": {
@@ -2511,8 +2511,8 @@
},
"show.php": {
"type": "-",
"size": 39716,
"lmtime": 1774295938464,
"size": 40747,
"lmtime": 1774474658482,
"modified": false
}
},
@@ -2630,8 +2630,8 @@
},
"printing.php": {
"type": "-",
"size": 8683,
"lmtime": 0,
"size": 10591,
"lmtime": 1774475356232,
"modified": false
},
"products.php": {
@@ -2670,16 +2670,16 @@
}
},
"automation": {
"index.php": {
"type": "-",
"size": 3962,
"lmtime": 1773789562003,
"modified": false
},
"form.php": {
"type": "-",
"size": 7536,
"lmtime": 1773789588610,
"size": 12406,
"lmtime": 1774475517368,
"modified": false
},
"index.php": {
"type": "-",
"size": 4020,
"lmtime": 1774474734632,
"modified": false
}
}
@@ -2688,8 +2688,8 @@
"routes": {
"web.php": {
"type": "-",
"size": 24661,
"lmtime": 1774305148776,
"size": 24767,
"lmtime": 1774475608068,
"modified": false
}
},
@@ -2744,9 +2744,23 @@
"Core": {
"Application.php": {
"type": "-",
"size": 8679,
"lmtime": 1772997978216,
"modified": true
"size": 9588,
"lmtime": 1774474842256,
"modified": false
},
"Constants": {
"IntegrationSources.php": {
"type": "-",
"size": 263,
"lmtime": 1773397435140,
"modified": false
},
"RedirectPaths.php": {
"type": "-",
"size": 643,
"lmtime": 1773397436702,
"modified": false
}
},
"Database": {
"ConnectionFactory.php": {
@@ -2871,20 +2885,6 @@
"lmtime": 1771460443424,
"modified": false
}
},
"Constants": {
"IntegrationSources.php": {
"type": "-",
"size": 263,
"lmtime": 1773397435140,
"modified": false
},
"RedirectPaths.php": {
"type": "-",
"size": 643,
"lmtime": 1773397436702,
"modified": false
}
}
},
"Modules": {
@@ -2949,8 +2949,8 @@
},
"CronHandlerFactory.php": {
"type": "-",
"size": 5705,
"lmtime": 1774293819740,
"size": 7691,
"lmtime": 1774475612061,
"modified": false
},
"CronJobProcessor.php": {
@@ -2991,8 +2991,8 @@
},
"ShipmentTrackingHandler.php": {
"type": "-",
"size": 1602,
"lmtime": 1774294151963,
"size": 3332,
"lmtime": 1774474758047,
"modified": false
},
"ShopProOfferTitlesRefreshHandler.php": {
@@ -3095,14 +3095,14 @@
},
"OrdersController.php": {
"type": "-",
"size": 33392,
"lmtime": 1774216760398,
"size": 33087,
"lmtime": 1774473628426,
"modified": false
},
"OrdersRepository.php": {
"type": "-",
"size": 32265,
"lmtime": 1774216750636,
"size": 34109,
"lmtime": 1774474648512,
"modified": false
},
"OrderStatusSyncService.php": {
@@ -3227,14 +3227,14 @@
},
"AllegroOrderImportService.php": {
"type": "-",
"size": 29642,
"lmtime": 1773397494713,
"modified": true
"size": 30679,
"lmtime": 1774474628606,
"modified": false
},
"AllegroOrdersSyncService.php": {
"type": "-",
"size": 8112,
"lmtime": 1773396210179,
"size": 8127,
"lmtime": 1774474632492,
"modified": false
},
"AllegroOrderSyncStateRepository.php": {
@@ -3263,9 +3263,9 @@
},
"AllegroStatusSyncService.php": {
"type": "-",
"size": 3870,
"lmtime": 1773397499705,
"modified": true
"size": 3885,
"lmtime": 1774474635936,
"modified": false
},
"AllegroTokenManager.php": {
"type": "-",
@@ -3401,8 +3401,8 @@
},
"PrintSettingsController.php": {
"type": "-",
"size": 3404,
"lmtime": 0,
"size": 4210,
"lmtime": 1774474891200,
"modified": false
},
"ReceiptConfigController.php": {
@@ -3611,8 +3611,8 @@
"Automation": {
"AutomationController.php": {
"type": "-",
"size": 11363,
"lmtime": 1773789536055,
"size": 14859,
"lmtime": 1774475481168,
"modified": false
},
"AutomationRepository.php": {
@@ -3623,8 +3623,34 @@
},
"AutomationService.php": {
"type": "-",
"size": 5382,
"lmtime": 1773790173434,
"size": 22368,
"lmtime": 1774475866269,
"modified": false
}
},
"Printing": {
"ApiKeyMiddleware.php": {
"type": "-",
"size": 1077,
"lmtime": 0,
"modified": false
},
"PrintApiController.php": {
"type": "-",
"size": 5405,
"lmtime": 1774473708117,
"modified": false
},
"PrintApiKeyRepository.php": {
"type": "-",
"size": 2143,
"lmtime": 0,
"modified": false
},
"PrintJobRepository.php": {
"type": "-",
"size": 4696,
"lmtime": 1774474858944,
"modified": false
}
}

BIN
delivery-tab-bug.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

View File

@@ -54,6 +54,11 @@ $eventLabels = [
</td>
<td style="white-space:nowrap">
<a href="/settings/automation/edit?id=<?= (int) ($rule['id'] ?? 0) ?>" class="btn btn--sm btn--secondary">Edytuj</a>
<form action="/settings/automation/duplicate" method="post" style="display:inline">
<input type="hidden" name="_token" value="<?= $e($csrfToken ?? '') ?>">
<input type="hidden" name="id" value="<?= (int) ($rule['id'] ?? 0) ?>">
<button type="submit" class="btn btn--sm btn--secondary">Duplikuj</button>
</form>
<form action="/settings/automation/toggle" method="post" style="display:inline">
<input type="hidden" name="_token" value="<?= $e($csrfToken ?? '') ?>">
<input type="hidden" name="id" value="<?= (int) ($rule['id'] ?? 0) ?>">

View File

@@ -74,27 +74,21 @@
</svg>
</summary>
<div class="sidebar__group-links">
<a class="sidebar__sublink<?= $currentMenu === 'settings' && $currentSettings === 'users' ? ' is-active' : '' ?>" href="/settings/users">
<?= $e($t('navigation.users')) ?>
</a>
<a class="sidebar__sublink<?= $currentMenu === 'settings' && $currentSettings === 'database' ? ' is-active' : '' ?>" href="/settings/database">
<?= $e($t('navigation.database')) ?>
</a>
<a class="sidebar__sublink<?= $currentMenu === 'settings' && $currentSettings === 'statuses' ? ' is-active' : '' ?>" href="/settings/statuses">
<?= $e($t('navigation.statuses')) ?>
</a>
<a class="sidebar__sublink<?= $currentMenu === 'settings' && $currentSettings === 'cron' ? ' is-active' : '' ?>" href="/settings/cron">
<?= $e($t('navigation.cron')) ?>
</a>
<a class="sidebar__sublink<?= $currentMenu === 'settings' && in_array($currentSettings, ['integrations', 'allegro', 'apaczka', 'inpost', 'shoppro'], true) ? ' is-active' : '' ?>" href="/settings/integrations">
<?= $e($t('navigation.integrations')) ?>
</a>
<a class="sidebar__sublink<?= $currentMenu === 'settings' && $currentSettings === 'company' ? ' is-active' : '' ?>" href="/settings/company">
<?= $e($t('navigation.company')) ?>
</a>
<a class="sidebar__sublink<?= $currentMenu === 'settings' && $currentSettings === 'accounting' ? ' is-active' : '' ?>" href="/settings/accounting">
<?= $e($t('navigation.accounting')) ?>
</a>
<a class="sidebar__sublink<?= $currentMenu === 'settings' && $currentSettings === 'users' ? ' is-active' : '' ?>" href="/settings/users">
<?= $e($t('navigation.users')) ?>
</a>
<a class="sidebar__sublink<?= $currentMenu === 'settings' && in_array($currentSettings, ['integrations', 'allegro', 'apaczka', 'inpost', 'shoppro'], true) ? ' is-active' : '' ?>" href="/settings/integrations">
<?= $e($t('navigation.integrations')) ?>
</a>
<a class="sidebar__sublink<?= $currentMenu === 'settings' && $currentSettings === 'statuses' ? ' is-active' : '' ?>" href="/settings/statuses">
<?= $e($t('navigation.statuses')) ?>
</a>
<a class="sidebar__sublink<?= $currentMenu === 'settings' && $currentSettings === 'email-mailboxes' ? ' is-active' : '' ?>" href="/settings/email-mailboxes">
Skrzynki pocztowe
</a>
@@ -104,12 +98,18 @@
<a class="sidebar__sublink<?= $currentMenu === 'settings' && $currentSettings === 'automation' ? ' is-active' : '' ?>" href="/settings/automation">
Zadania automatyczne
</a>
<a class="sidebar__sublink<?= $currentMenu === 'settings' && $currentSettings === 'cron' ? ' is-active' : '' ?>" href="/settings/cron">
<?= $e($t('navigation.cron')) ?>
</a>
<a class="sidebar__sublink<?= $currentMenu === 'settings' && $currentSettings === 'printing' ? ' is-active' : '' ?>" href="/settings/printing">
Drukowanie
</a>
<a class="sidebar__sublink<?= $currentMenu === 'settings' && $currentSettings === 'delivery-status-mappings' ? ' is-active' : '' ?>" href="/settings/delivery-status-mappings">
Mapowanie statusów dostawy
</a>
<a class="sidebar__sublink<?= $currentMenu === 'settings' && $currentSettings === 'database' ? ' is-active' : '' ?>" href="/settings/database">
<?= $e($t('navigation.database')) ?>
</a>
</div>
</details>
</nav>

View File

@@ -362,8 +362,8 @@ $orderproStatuses = is_array($orderproStatuses ?? null) ? $orderproStatuses : []
<input type="hidden" name="allegro_service_name[]" class="dm-hidden-service-name" value="<?= $e($currentServiceName) ?>">
<?php // Allegro searchable select ?>
<div class="dm-allegro-panel dm-searchable-select" data-current-id="<?= $e(($currentCarrier === 'allegro' || $currentCarrier === 'inpost') ? $currentMethodId : '') ?>" data-current-name="<?= $e(($currentCarrier === 'allegro' || $currentCarrier === 'inpost') ? $currentServiceName : '') ?>" style="<?= ($currentCarrier !== 'allegro' && $currentCarrier !== 'inpost') ? 'display:none' : '' ?>">
<input type="text" class="form-control dm-search-input" placeholder="<?= $e($t('settings.allegro.delivery.fields.search_placeholder')) ?>" value="<?= $e(($currentCarrier === 'allegro' || $currentCarrier === 'inpost') ? $currentServiceName : '') ?>" autocomplete="off">
<div class="dm-allegro-panel dm-searchable-select" data-current-id="<?= $e($currentCarrier === 'allegro' ? $currentMethodId : '') ?>" data-current-name="<?= $e($currentCarrier === 'allegro' ? $currentServiceName : '') ?>" style="<?= $currentCarrier !== 'allegro' ? 'display:none' : '' ?>">
<input type="text" class="form-control dm-search-input" placeholder="<?= $e($t('settings.allegro.delivery.fields.search_placeholder')) ?>" value="<?= $e($currentCarrier === 'allegro' ? $currentServiceName : '') ?>" autocomplete="off">
<div class="searchable-select__dropdown dm-dropdown">
<div class="searchable-select__option dm-option-clear" data-value="" data-label="" data-credentials-id="" data-carrier-id="">
<em class="muted">-- <?= $e($t('settings.allegro.delivery.fields.no_mapping')) ?> --</em>
@@ -559,7 +559,8 @@ $orderproStatuses = is_array($orderproStatuses ?? null) ? $orderproStatuses : []
};
}
document.querySelectorAll('.dm-inpost-select, .dm-apaczka-select').forEach(function (selectEl) {
// Filter only for Apaczka (many services); InPost has few options
document.querySelectorAll('.dm-apaczka-select').forEach(function (selectEl) {
attachSelectFilter(selectEl);
});

View File

@@ -53,6 +53,11 @@ $attachmentTypes = is_array($attachmentTypes ?? null) ? $attachmentTypes : [];
</td>
<td style="white-space:nowrap">
<a href="/settings/email-templates?edit=<?= (int) ($tpl['id'] ?? 0) ?>" class="btn btn--sm btn--secondary">Edytuj</a>
<form action="/settings/email-templates/duplicate" method="post" style="display:inline">
<input type="hidden" name="_token" value="<?= $e($csrfToken ?? '') ?>">
<input type="hidden" name="id" value="<?= (int) ($tpl['id'] ?? 0) ?>">
<button type="submit" class="btn btn--sm btn--secondary">Duplikuj</button>
</form>
<button type="button" class="btn btn--sm btn--secondary js-toggle-btn"
data-id="<?= (int) ($tpl['id'] ?? 0) ?>"
data-active="<?= (int) ($tpl['is_active'] ?? 0) ?>">

View File

@@ -391,6 +391,7 @@ return static function (Application $app): void {
$router->get('/settings/email-templates', [$emailTemplateController, 'index'], [$authMiddleware]);
$router->post('/settings/email-templates/save', [$emailTemplateController, 'save'], [$authMiddleware]);
$router->post('/settings/email-templates/delete', [$emailTemplateController, 'delete'], [$authMiddleware]);
$router->post('/settings/email-templates/duplicate', [$emailTemplateController, 'duplicate'], [$authMiddleware]);
$router->post('/settings/email-templates/toggle', [$emailTemplateController, 'toggleStatus'], [$authMiddleware]);
$router->post('/settings/email-templates/preview', [$emailTemplateController, 'preview'], [$authMiddleware]);
$router->get('/settings/email-templates/variables', [$emailTemplateController, 'getVariables'], [$authMiddleware]);
@@ -400,6 +401,7 @@ return static function (Application $app): void {
$router->get('/settings/automation/edit', [$automationController, 'edit'], [$authMiddleware]);
$router->post('/settings/automation/update', [$automationController, 'update'], [$authMiddleware]);
$router->post('/settings/automation/delete', [$automationController, 'destroy'], [$authMiddleware]);
$router->post('/settings/automation/duplicate', [$automationController, 'duplicate'], [$authMiddleware]);
$router->post('/settings/automation/toggle', [$automationController, 'toggleStatus'], [$authMiddleware]);
$router->get('/settings/delivery-status-mappings', [$deliveryStatusMappingController, 'index'], [$authMiddleware]);
$router->post('/settings/delivery-status-mappings/save', [$deliveryStatusMappingController, 'save'], [$authMiddleware]);

View File

@@ -160,6 +160,29 @@ final class AutomationController
return Response::redirect('/settings/automation');
}
public function duplicate(Request $request): Response
{
$error = $this->validateCsrf($request);
if ($error !== null) {
return $error;
}
$id = (int) $request->input('id', '0');
if ($id <= 0) {
Flash::set('settings.automation.error', 'Nieprawidlowy identyfikator');
return Response::redirect('/settings/automation');
}
try {
$this->repository->duplicate($id);
Flash::set('settings.automation.success', 'Zadanie zostalo zduplikowane');
} catch (Throwable) {
Flash::set('settings.automation.error', 'Blad duplikowania zadania');
}
return Response::redirect('/settings/automation');
}
public function toggleStatus(Request $request): Response
{
$error = $this->validateCsrf($request);

View File

@@ -126,6 +126,28 @@ final class AutomationRepository
$statement->execute(['id' => $id]);
}
public function duplicate(int $id): int
{
$source = $this->findById($id);
if ($source === null) {
throw new \RuntimeException('Zadanie nie istnieje');
}
$conditions = array_map(static function (array $c): array {
return ['type' => $c['condition_type'], 'value' => $c['condition_value']];
}, $source['conditions']);
$actions = array_map(static function (array $a): array {
return ['type' => $a['action_type'], 'config' => $a['action_config']];
}, $source['actions']);
return $this->create(
['name' => 'Kopia — ' . $source['name'], 'event_type' => $source['event_type'], 'is_active' => 0],
$conditions,
$actions
);
}
public function toggleActive(int $id): void
{
$statement = $this->pdo->prepare(

View File

@@ -196,6 +196,29 @@ final class EmailTemplateController
}
}
public function duplicate(Request $request): Response
{
if (!Csrf::validate((string) $request->input('_token', ''))) {
Flash::set('settings.email_templates.error', 'Nieprawidlowy token CSRF');
return Response::redirect('/settings/email-templates');
}
$id = (int) $request->input('id', '0');
if ($id <= 0) {
Flash::set('settings.email_templates.error', 'Nieprawidlowy identyfikator szablonu');
return Response::redirect('/settings/email-templates');
}
try {
$this->repository->duplicate($id);
Flash::set('settings.email_templates.success', 'Szablon zostal zduplikowany');
} catch (Throwable) {
Flash::set('settings.email_templates.error', 'Blad duplikowania szablonu');
}
return Response::redirect('/settings/email-templates');
}
public function preview(Request $request): Response
{
if (!Csrf::validate((string) $request->input('_token', ''))) {

View File

@@ -119,4 +119,24 @@ final class EmailTemplateRepository
);
$statement->execute(['id' => $id]);
}
public function duplicate(int $id): void
{
$source = $this->findById($id);
if ($source === null) {
throw new \RuntimeException('Szablon nie istnieje');
}
$statement = $this->pdo->prepare(
'INSERT INTO email_templates (name, subject, body_html, mailbox_id, attachment_1, is_active)
VALUES (:name, :subject, :body_html, :mailbox_id, :attachment_1, 0)'
);
$statement->execute([
'name' => 'Kopia — ' . $source['name'],
'subject' => $source['subject'],
'body_html' => $source['body_html'],
'mailbox_id' => $source['mailbox_id'],
'attachment_1' => $source['attachment_1'],
]);
}
}