update
This commit is contained in:
@@ -1335,8 +1335,9 @@ class ProductRepository
|
||||
$this->saveImagesOrder( $productId, $d['gallery_order'] );
|
||||
}
|
||||
|
||||
// Zapisz custom fields tylko gdy jawnie podane (partial update przez API może nie zawierać tego klucza)
|
||||
if ( array_key_exists( 'custom_field_name', $d ) ) {
|
||||
// Zapisz custom fields tylko gdy formularz edycji renderował sekcję (marker hidden field)
|
||||
// API partial update nie zawiera tego markera — custom fields pominięte
|
||||
if ( array_key_exists( 'custom_field_name_present', $d ) ) {
|
||||
$this->saveCustomFields( $productId, $d['custom_field_name'] ?? [], $d['custom_field_type'] ?? [], $d['custom_field_required'] ?? [] );
|
||||
}
|
||||
|
||||
@@ -2204,6 +2205,44 @@ class ProductRepository
|
||||
] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Pobiera nazwy etykiet custom_label_0..4 z bazy ustawien.
|
||||
*
|
||||
* @return array<string, string>
|
||||
*/
|
||||
public function customLabelNames(): array
|
||||
{
|
||||
$names = [];
|
||||
for ( $index = 0; $index < 5; $index++ ) {
|
||||
$fieldName = 'custom_label_' . $index;
|
||||
$names[$fieldName] = 'Custom label ' . $index;
|
||||
}
|
||||
|
||||
$settingsKeys = [];
|
||||
for ( $index = 0; $index < 5; $index++ ) {
|
||||
$settingsKeys[] = 'custom_label_' . $index . '_name';
|
||||
$settingsKeys[] = 'google_custom_label_' . $index . '_name';
|
||||
}
|
||||
|
||||
$settingsRows = $this->db->select( 'pp_settings', [ 'param', 'value' ], [ 'param' => $settingsKeys ] );
|
||||
if ( is_array( $settingsRows ) ) {
|
||||
foreach ( $settingsRows as $settingRow ) {
|
||||
$param = (string) ( $settingRow['param'] ?? '' );
|
||||
$value = trim( (string) ( $settingRow['value'] ?? '' ) );
|
||||
|
||||
if ( $value === '' ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( preg_match( '/^(?:google_)?custom_label_([0-4])_name$/', $param, $match ) ) {
|
||||
$names[ 'custom_label_' . $match[1] ] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $names;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pobiera sugestie custom label.
|
||||
*/
|
||||
|
||||
@@ -184,8 +184,16 @@ class ScontainersController
|
||||
}
|
||||
|
||||
$data = $result['data'];
|
||||
$containerId = (int)($data['id'] ?? 0);
|
||||
if ($containerId <= 0) {
|
||||
$routeId = (int)\Shared\Helpers\Helpers::get('id');
|
||||
if ($routeId > 0) {
|
||||
$containerId = $routeId;
|
||||
}
|
||||
}
|
||||
|
||||
$savedId = $this->repository->save([
|
||||
'id' => (int)($data['id'] ?? 0),
|
||||
'id' => $containerId,
|
||||
'status' => $data['status'] ?? 0,
|
||||
'show_title' => $data['show_title'] ?? 0,
|
||||
'translations' => $data['translations'] ?? [],
|
||||
@@ -240,7 +248,6 @@ class ScontainersController
|
||||
];
|
||||
|
||||
$fields = [
|
||||
FormField::hidden('id', $id),
|
||||
FormField::langSection('translations', 'content', [
|
||||
FormField::text('title', [
|
||||
'label' => 'Tytul',
|
||||
@@ -283,7 +290,7 @@ class ScontainersController
|
||||
$actionUrl,
|
||||
'/admin/scontainers/list/',
|
||||
true,
|
||||
[],
|
||||
['id' => $id],
|
||||
$languages,
|
||||
$errors
|
||||
);
|
||||
|
||||
@@ -18,6 +18,8 @@ use admin\Support\TableListRequestFactory;
|
||||
*/
|
||||
class ShopProductController
|
||||
{
|
||||
private const CUSTOM_LABELS_SESSION_KEY = 'shop_product_show_custom_labels';
|
||||
|
||||
private ProductRepository $repository;
|
||||
private IntegrationsRepository $integrationsRepository;
|
||||
private LanguagesRepository $languagesRepository;
|
||||
@@ -39,6 +41,8 @@ class ShopProductController
|
||||
$apiloEnabled = $this->integrationsRepository->getSetting( 'apilo', 'enabled' );
|
||||
$shopproEnabled = $this->integrationsRepository->getSetting( 'shoppro', 'enabled' );
|
||||
$dlang = $this->languagesRepository->defaultLanguage();
|
||||
$customLabelsEnabled = $this->customLabelsEnabled();
|
||||
$customLabelNames = $this->repository->customLabelNames();
|
||||
|
||||
$sortableColumns = [ 'id', 'name', 'price_brutto', 'status', 'promoted', 'quantity' ];
|
||||
|
||||
@@ -98,6 +102,10 @@ class ShopProductController
|
||||
. '<small class="text-muted product-categories product-categories--cats" title="' . $categories . '">' . $categories . '</small>'
|
||||
. '<small class="text-muted product-categories">SKU: ' . $sku . ', EAN: ' . $ean . '</small>';
|
||||
|
||||
if ( $customLabelsEnabled ) {
|
||||
$nameHtml .= $this->renderCustomLabelsEditor( $product, $id, $customLabelNames );
|
||||
}
|
||||
|
||||
$priceHtml = '<input type="text" class="product-price form-control text-right" product-id="' . $id . '" value="' . htmlspecialchars( (string) $product['price_brutto'], ENT_QUOTES, 'UTF-8' ) . '" style="width: 75px;">';
|
||||
$promoHtml = '<input type="text" class="product-price-promo form-control text-right" product-id="' . $id . '" value="' . htmlspecialchars( (string) $product['price_brutto_promo'], ENT_QUOTES, 'UTF-8' ) . '" style="width: 75px;">';
|
||||
$promotedHtml = $product['promoted'] ? '<span class="text-success text-bold">tak</span>' : 'nie';
|
||||
@@ -195,11 +203,25 @@ class ShopProductController
|
||||
'viewModel' => $viewModel,
|
||||
'apilo_enabled' => $apiloEnabled,
|
||||
'shoppro_enabled' => $shopproEnabled,
|
||||
'custom_labels_enabled' => $customLabelsEnabled,
|
||||
] );
|
||||
}
|
||||
|
||||
// ─── Krok 7: Edycja i zapis ─────────────────────────────────────
|
||||
|
||||
/**
|
||||
* AJAX: przelacza widok custom labels na liscie produktow i zapisuje stan w sesji.
|
||||
*/
|
||||
public function product_custom_labels_toggle(): void
|
||||
{
|
||||
$currentState = $this->customLabelsEnabled();
|
||||
$newState = $currentState ? 0 : 1;
|
||||
\Shared\Helpers\Helpers::set_session( self::CUSTOM_LABELS_SESSION_KEY, $newState );
|
||||
|
||||
echo json_encode( [ 'status' => 'ok', 'enabled' => (bool) $newState ] );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formularz edycji produktu.
|
||||
*/
|
||||
@@ -699,7 +721,8 @@ class ShopProductController
|
||||
|
||||
private function renderCustomFieldsBox( array $product ): string
|
||||
{
|
||||
$html = '<a href="#" class="btn btn-success" id="add_custom_field"><i class="fa fa-plus"></i> dodaj niestandardowe pole</a>';
|
||||
$html = '<input type="hidden" name="custom_field_name_present" value="1">';
|
||||
$html .= '<a href="#" class="btn btn-success" id="add_custom_field"><i class="fa fa-plus"></i> dodaj niestandardowe pole</a>';
|
||||
$html .= '<div class="additional_fields pt-3">';
|
||||
|
||||
$customFields = is_array( $product['custom_fields'] ?? null ) ? $product['custom_fields'] : [];
|
||||
@@ -896,9 +919,15 @@ class ShopProductController
|
||||
public function product_custom_label_suggestions(): void
|
||||
{
|
||||
$response = [ 'status' => 'error', 'msg' => 'Podczas pobierania sugestii dla custom label wystąpił błąd. Proszę spróbować ponownie.' ];
|
||||
$labelType = (string) \Shared\Helpers\Helpers::get( 'label_type' );
|
||||
|
||||
$suggestions = $this->repository->customLabelSuggestions( \Shared\Helpers\Helpers::get( 'custom_label' ), \Shared\Helpers\Helpers::get( 'label_type' ) );
|
||||
if ( $suggestions ) {
|
||||
if ( !$this->isAllowedCustomLabelType( $labelType ) ) {
|
||||
echo json_encode( $response );
|
||||
exit;
|
||||
}
|
||||
|
||||
$suggestions = $this->repository->customLabelSuggestions( (string) \Shared\Helpers\Helpers::get( 'custom_label' ), $labelType );
|
||||
if ( is_array( $suggestions ) ) {
|
||||
$response = [ 'status' => 'ok', 'suggestions' => $suggestions ];
|
||||
}
|
||||
|
||||
@@ -912,8 +941,14 @@ class ShopProductController
|
||||
public function product_custom_label_save(): void
|
||||
{
|
||||
$response = [ 'status' => 'error', 'msg' => 'Podczas zapisywania custom label wystąpił błąd. Proszę spróbować ponownie.' ];
|
||||
$labelType = (string) \Shared\Helpers\Helpers::get( 'label_type' );
|
||||
|
||||
if ( $this->repository->saveCustomLabel( (int) \Shared\Helpers\Helpers::get( 'product_id' ), \Shared\Helpers\Helpers::get( 'custom_label' ), \Shared\Helpers\Helpers::get( 'label_type' ) ) ) {
|
||||
if ( !$this->isAllowedCustomLabelType( $labelType ) ) {
|
||||
echo json_encode( $response );
|
||||
exit;
|
||||
}
|
||||
|
||||
if ( $this->repository->saveCustomLabel( (int) \Shared\Helpers\Helpers::get( 'product_id' ), (string) \Shared\Helpers\Helpers::get( 'custom_label' ), $labelType ) ) {
|
||||
$response = [ 'status' => 'ok' ];
|
||||
}
|
||||
|
||||
@@ -1196,4 +1231,36 @@ class ShopProductController
|
||||
echo json_encode( [ 'status' => 'ok', 'products' => $products ] );
|
||||
exit;
|
||||
}
|
||||
|
||||
private function customLabelsEnabled(): bool
|
||||
{
|
||||
return isset( $_SESSION[ self::CUSTOM_LABELS_SESSION_KEY ] ) && (int) $_SESSION[ self::CUSTOM_LABELS_SESSION_KEY ] === 1;
|
||||
}
|
||||
|
||||
private function isAllowedCustomLabelType(string $labelType): bool
|
||||
{
|
||||
return in_array( $labelType, [ 'custom_label_0', 'custom_label_1', 'custom_label_2', 'custom_label_3', 'custom_label_4' ], true );
|
||||
}
|
||||
|
||||
private function renderCustomLabelsEditor(array $product, int $productId, array $customLabelNames): string
|
||||
{
|
||||
$customLabelsHtml = '<div class="custom-labels mt10">';
|
||||
|
||||
for ( $index = 0; $index < 5; $index++ ) {
|
||||
$fieldName = 'custom_label_' . $index;
|
||||
$labelText = htmlspecialchars( (string) ( $customLabelNames[$fieldName] ?? 'Custom label ' . $index ), ENT_QUOTES, 'UTF-8' );
|
||||
$valueText = htmlspecialchars( (string) ( $product[$fieldName] ?? '' ), ENT_QUOTES, 'UTF-8' );
|
||||
|
||||
$customLabelsHtml .= '<div class="' . $fieldName . '_container">';
|
||||
$customLabelsHtml .= '<small class="text-muted">' . $labelText . '</small>';
|
||||
$datalistId = 'custom-label-list-' . $productId . '-' . $fieldName;
|
||||
$customLabelsHtml .= '<input type="text" class="form-control input-sm product-custom-label" data-label-type="' . $fieldName . '" data-product-id="' . $productId . '" data-datalist-id="' . $datalistId . '" list="' . $datalistId . '" value="' . $valueText . '" placeholder="' . $labelText . '">';
|
||||
$customLabelsHtml .= '<datalist id="' . $datalistId . '"></datalist>';
|
||||
$customLabelsHtml .= '<div class="' . $fieldName . '_suggestions custom-label-suggestions"></div>';
|
||||
$customLabelsHtml .= '</div>';
|
||||
}
|
||||
|
||||
$customLabelsHtml .= '</div>';
|
||||
return $customLabelsHtml;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -280,20 +280,71 @@ class ShopBasketController
|
||||
$client = \Shared\Helpers\Helpers::get_session( 'client' );
|
||||
$orderSubmitToken = $this->createOrderSubmitToken();
|
||||
|
||||
$basket = \Shared\Helpers\Helpers::get_session( 'basket' );
|
||||
$coupon = \Shared\Helpers\Helpers::get_session( 'coupon' );
|
||||
$transport = ( new \Domain\Transport\TransportRepository( $GLOBALS['mdb'] ) )->findActiveByIdCached( \Shared\Helpers\Helpers::get_session( 'basket-transport-method-id' ) );
|
||||
|
||||
$productsSummary = (float)\Domain\Basket\BasketCalculator::summaryPrice( $basket, $coupon );
|
||||
$freeDeliveryThreshold = isset( $settings['free_delivery'] ) ? (float)$settings['free_delivery'] : 0.0;
|
||||
$transportCalc = $this->calculateTransportCostForSummary( $transport, $productsSummary, $freeDeliveryThreshold );
|
||||
|
||||
return \Shared\Tpl\Tpl::view( 'shop-basket/summary-view', [
|
||||
'lang_id' => $lang_id,
|
||||
'client' => \Shared\Helpers\Helpers::get_session( 'client' ),
|
||||
'basket' => \Shared\Helpers\Helpers::get_session( 'basket' ),
|
||||
'transport' => ( new \Domain\Transport\TransportRepository( $GLOBALS['mdb'] ) )->findActiveByIdCached( \Shared\Helpers\Helpers::get_session( 'basket-transport-method-id' ) ),
|
||||
'basket' => $basket,
|
||||
'transport' => $transport,
|
||||
'transport_cost_effective' => $transportCalc['transport_cost_effective'],
|
||||
'free_delivery_applies' => $transportCalc['free_delivery_applies'],
|
||||
'payment_method' => $this->paymentMethodRepository->paymentMethodCached( (int)\Shared\Helpers\Helpers::get_session( 'basket-payment-method-id' ) ),
|
||||
'addresses' => ( new \Domain\Client\ClientRepository( $GLOBALS['mdb'] ) )->clientAddresses( (int)$client['id'] ),
|
||||
'settings' => $settings,
|
||||
'coupon' => \Shared\Helpers\Helpers::get_session( 'coupon' ),
|
||||
'coupon' => $coupon,
|
||||
'basket_message' => \Shared\Helpers\Helpers::get_session( 'basket_message' ),
|
||||
'order_submit_token' => $orderSubmitToken
|
||||
] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Wylicza efektywny koszt transportu dla widoku /koszyk-podsumowanie.
|
||||
* Koszt spada do 0, gdy transport ma flage delivery_free=1 ORAZ wartosc koszyka
|
||||
* (po kuponie) osiaga prog darmowej dostawy $freeDeliveryThreshold.
|
||||
*
|
||||
* @param array|null $transport Aktywny transport (lub null gdy nie wybrany)
|
||||
* @param float $productsSummary Wartosc koszyka po kuponie
|
||||
* @param float $freeDeliveryThreshold Prog darmowej dostawy z settings.free_delivery
|
||||
* @return array{transport_cost_effective: float, free_delivery_applies: bool}
|
||||
*/
|
||||
protected function calculateTransportCostForSummary( $transport, $productsSummary, $freeDeliveryThreshold )
|
||||
{
|
||||
if ( !is_array( $transport ) )
|
||||
{
|
||||
return [
|
||||
'transport_cost_effective' => 0.0,
|
||||
'free_delivery_applies' => false,
|
||||
];
|
||||
}
|
||||
|
||||
$deliveryFree = isset( $transport['delivery_free'] ) ? (int)$transport['delivery_free'] : 0;
|
||||
$cost = isset( $transport['cost'] ) ? (float)$transport['cost'] : 0.0;
|
||||
|
||||
$applies = false;
|
||||
if ( $deliveryFree === 1 && $freeDeliveryThreshold > 0 )
|
||||
{
|
||||
$summaryNormalized = \Shared\Helpers\Helpers::normalize_decimal( $productsSummary );
|
||||
$thresholdNormalized = \Shared\Helpers\Helpers::normalize_decimal( $freeDeliveryThreshold );
|
||||
|
||||
if ( $summaryNormalized >= $thresholdNormalized )
|
||||
{
|
||||
$applies = true;
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'transport_cost_effective' => $applies ? 0.0 : $cost,
|
||||
'free_delivery_applies' => $applies,
|
||||
];
|
||||
}
|
||||
|
||||
public function basketSave()
|
||||
{
|
||||
$orderSubmitToken = (string)\Shared\Helpers\Helpers::get( 'order_submit_token', true );
|
||||
|
||||
Reference in New Issue
Block a user