Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
77.37% |
106 / 137 |
n/a |
0 / 0 |
CRAP | n/a |
0 / 0 |
||
| 1 | <?php |
| 2 | |
| 3 | /** |
| 4 | * @file |
| 5 | * Place action: load product details by objids (productnfo). |
| 6 | */ |
| 7 | |
| 8 | declare(strict_types=1); |
| 9 | |
| 10 | require_once __DIR__.'/../../Legacy/common/session.php'; |
| 11 | require_once __DIR__.'/../../Legacy/model/synchloader.php'; |
| 12 | |
| 13 | /** |
| 14 | * Action callable: load product details by objids (POST body). |
| 15 | * |
| 16 | * @param string $body JSON array of product objids. |
| 17 | * @param array<string, mixed> $query Query params: device, session, place, dateini, dateend. |
| 18 | * @param \ConxHelper $conx Connection helper. |
| 19 | * @param \Psr\Log\LoggerInterface $logger Logger. |
| 20 | * @param \UppServices\SessionService $sessionService Session service for check. |
| 21 | * @return array{output: string, contentType: string} |
| 22 | */ |
| 23 | return function (string $body, array $query, \ConxHelper $conx, \Psr\Log\LoggerInterface $logger, \UppServices\SessionService $sessionService): array { |
| 24 | $retval = \ApplicationError::Success; |
| 25 | $updates = []; |
| 26 | $logPrefix = '[place/productnfo]'; |
| 27 | |
| 28 | $datamodel = json_decode('{ |
| 29 | "data": { |
| 30 | "PRODUCT": [ |
| 31 | { "direction": ">", "target": "I18NVALUE", "by": "name", "reason": null }, |
| 32 | { "direction": ">", "target": "I18NVALUE", "by": "description", "reason": null }, |
| 33 | { "direction": "<", "target": "TICKETPRODUCT", "by": "product", "reason": "requested", "sql": "and TICKETPRODUCT.status = \'AC\'" } |
| 34 | ], |
| 35 | "TICKETPRODUCT": [ |
| 36 | { "direction": ">", "target": "TICKET", "by": "ticket", "reason": null }, |
| 37 | { "direction": ">", "target": "PRODUCT", "by": "product", "reason": null }, |
| 38 | { "direction": ">", "target": "TICKETOFFER", "by": "offer", "reason": null }, |
| 39 | { "direction": "<", "target": "TICKETOPTION", "by": "product", "reason": "options" } |
| 40 | ], |
| 41 | "TICKETOPTION": [ |
| 42 | { "direction": ">", "target": "TICKETPRODUCT", "by": "product", "reason": null }, |
| 43 | { "direction": ">", "target": "PRODUCTOPT", "by": "prodopt", "reason": null } |
| 44 | ], |
| 45 | "PRODUCTOPT": [ |
| 46 | { "direction": ">", "target": "I18NVALUE", "by": "name", "reason": null }, |
| 47 | { "direction": ">", "target": "I18NVALUE", "by": "description", "reason": null }, |
| 48 | { "direction": ">", "target": "PRODUCT", "by": "product", "reason": null }, |
| 49 | { "direction": ">", "target": "CATEGORY", "by": "category", "reason": null } |
| 50 | ], |
| 51 | "CATEGORY": [ |
| 52 | { "direction": ">", "target": "I18NVALUE", "by": "name", "reason": null }, |
| 53 | { "direction": ">", "target": "PRODUCT", "by": "product", "reason": null } |
| 54 | ], |
| 55 | "PRESELECT": [ |
| 56 | { "direction": ">", "target": "I18NVALUE", "by": "name", "reason": null }, |
| 57 | { "direction": ">", "target": "I18NVALUE", "by": "description", "reason": null }, |
| 58 | { "direction": ">", "target": "PRODUCT", "by": "product", "reason": null } |
| 59 | ], |
| 60 | "TICKETOFFER": [ |
| 61 | { "direction": ">", "target": "OFFER", "by": "offer", "reason": null } |
| 62 | ], |
| 63 | "TICKETEXTRA": [ |
| 64 | { "direction": ">", "target": "EXTRA", "by": "extra", "reason": null } |
| 65 | ] |
| 66 | } |
| 67 | }', true); |
| 68 | |
| 69 | $countByTable = static function (array $rows): array { |
| 70 | $counts = []; |
| 71 | foreach ($rows as $row) { |
| 72 | $table = (string) ($row['table'] ?? 'unknown'); |
| 73 | $counts[$table] = ($counts[$table] ?? 0) + 1; |
| 74 | } |
| 75 | return $counts; |
| 76 | }; |
| 77 | |
| 78 | $logger->info($logPrefix.' request query='.json_encode([ |
| 79 | 'place' => $query['place'] ?? null, |
| 80 | 'dateini' => $query['dateini'] ?? null, |
| 81 | 'dateend' => $query['dateend'] ?? null, |
| 82 | 'device' => $query['device'] ?? null, |
| 83 | 'session' => isset($query['session']) ? '(set)' : null, |
| 84 | 'lang' => $query['lang'] ?? null, |
| 85 | ], JSON_UNESCAPED_UNICODE)); |
| 86 | $logger->info($logPrefix.' request body='.$body); |
| 87 | |
| 88 | if (!$body) { |
| 89 | $logger->error($logPrefix.' No post data provided.'); |
| 90 | $retval = \ApplicationError::Parameters; |
| 91 | } |
| 92 | |
| 93 | if (\success($retval)) { |
| 94 | $products = json_decode($body, true); |
| 95 | if (!is_array($products)) { |
| 96 | $logger->error($logPrefix.' Invalid JSON format in post request data'); |
| 97 | $retval = \ApplicationError::Parameters; |
| 98 | } else { |
| 99 | $_objids = implode(', ', array_map('intval', $products)); |
| 100 | $logger->info($logPrefix.' product objids=['.$_objids.'] count='.count($products)); |
| 101 | } |
| 102 | } |
| 103 | |
| 104 | if (\success($retval)) { |
| 105 | $device = $query['device'] ?? null; |
| 106 | if (!$device) { |
| 107 | $logger->error("No device provided on URL. Operation cancelled."); |
| 108 | $retval = \ApplicationError::Parameters; |
| 109 | } |
| 110 | } |
| 111 | |
| 112 | if (\success($retval)) { |
| 113 | $session = $query['session'] ?? null; |
| 114 | if (!$session) { |
| 115 | $logger->error("No session provided on URL. Operation cancelled."); |
| 116 | $retval = \ApplicationError::Parameters; |
| 117 | } else { |
| 118 | $userid = null; |
| 119 | $sessionObjid = null; |
| 120 | $retval = $sessionService->checkSession($conx, (string) $session, (string) $device, $userid, $sessionObjid); |
| 121 | } |
| 122 | } |
| 123 | |
| 124 | if (\success($retval)) { |
| 125 | $place = $query['place'] ?? null; |
| 126 | if (is_array($place)) { |
| 127 | $place = (string) ($place[0] ?? ''); |
| 128 | } |
| 129 | if ($place === null || $place === '') { |
| 130 | $logger->error("Mandatory argument 'place' not provided in url parameters."); |
| 131 | $retval = \ApplicationError::Parameters; |
| 132 | } |
| 133 | } |
| 134 | |
| 135 | $ini = null; |
| 136 | $end = null; |
| 137 | if (\success($retval)) { |
| 138 | $ini = $query['dateini'] ?? null; |
| 139 | if ($ini === null) { |
| 140 | $logger->error("Mandatory argument 'dateini' not provided in url parameters."); |
| 141 | $retval = \ApplicationError::Parameters; |
| 142 | } |
| 143 | } |
| 144 | if (\success($retval)) { |
| 145 | $end = $query['dateend'] ?? null; |
| 146 | if ($end === null) { |
| 147 | $logger->error("Mandatory argument 'dateend' not provided in url parameters."); |
| 148 | $retval = \ApplicationError::Parameters; |
| 149 | } |
| 150 | } |
| 151 | |
| 152 | if (\success($retval)) { |
| 153 | $conx->tenant = $place; |
| 154 | } |
| 155 | |
| 156 | if (\success($retval) && $conx->tenant === null) { |
| 157 | $logger->error("Connection to place database failed."); |
| 158 | $retval = \ApplicationError::Database; |
| 159 | } |
| 160 | |
| 161 | $results = []; |
| 162 | if (\success($retval)) { |
| 163 | $GLOBALS['session'] = $session; |
| 164 | $datamodelData = is_array($datamodel) ? ($datamodel['data'] ?? null) : null; |
| 165 | if (!is_array($datamodelData)) { |
| 166 | $logger->error($logPrefix.' Invalid productnfo datamodel.'); |
| 167 | $retval = \ApplicationError::Generic; |
| 168 | } else { |
| 169 | $logger->info($logPrefix.' load_updates table=PRODUCT timestamp=0 products=['.$_objids.']'); |
| 170 | $retval = \load_updates($conx, $datamodelData, 'PRODUCT', $products, 0, $results); |
| 171 | $logger->info($logPrefix.' load_updates errorcode='.$retval.' rows='.count($results).' by_table='.json_encode($countByTable($results), JSON_UNESCAPED_UNICODE)); |
| 172 | } |
| 173 | } |
| 174 | |
| 175 | $allowedTicketStatuses = ['AC', 'RD', 'PP', 'PD']; |
| 176 | |
| 177 | if (\success($retval)) { |
| 178 | // Same period criterion as place/productlst (TICKETPRODUCT.created), not updated. |
| 179 | $iniTs = is_numeric($ini) ? (int) $ini : strtotime((string) $ini); |
| 180 | $endTs = is_numeric($end) ? (int) $end : strtotime((string) $end); |
| 181 | $periodLabel = static function ($ts): string { |
| 182 | return ($ts !== false) ? date('Y-m-d H:i:s', (int) $ts) : 'invalid'; |
| 183 | }; |
| 184 | $logger->info($logPrefix.' period filter iniTs='.$iniTs.' ('.$periodLabel($iniTs).') endTs='.$endTs.' ('.$periodLabel($endTs).')'); |
| 185 | $ticketAllowed = []; |
| 186 | foreach ($results as $row) { |
| 187 | if (($row['table'] ?? '') !== 'TICKET') { |
| 188 | continue; |
| 189 | } |
| 190 | $status = (string) ($row['status'] ?? ''); |
| 191 | if (in_array($status, $allowedTicketStatuses, true)) { |
| 192 | $ticketAllowed[(int) $row['objid']] = true; |
| 193 | } |
| 194 | } |
| 195 | $ticketProductsInPeriod = []; |
| 196 | $ticketProductsSkipped = []; |
| 197 | foreach ($results as $row) { |
| 198 | if (($row['table'] ?? '') !== 'TICKETPRODUCT') { |
| 199 | continue; |
| 200 | } |
| 201 | $objid = (int) $row['objid']; |
| 202 | $ticketId = (int) ($row['ticket'] ?? 0); |
| 203 | if ($ticketId > 0 && !isset($ticketAllowed[$ticketId])) { |
| 204 | $ticketProductsSkipped[$objid] = 'ticket_not_allowed:'.$ticketId; |
| 205 | continue; |
| 206 | } |
| 207 | $createdRaw = $row['created'] ?? null; |
| 208 | if ($createdRaw === null || $createdRaw === '') { |
| 209 | $ticketProductsSkipped[$objid] = 'no_created'; |
| 210 | continue; |
| 211 | } |
| 212 | $cdt = strtotime((string) $createdRaw); |
| 213 | if ($cdt === false) { |
| 214 | $ticketProductsSkipped[$objid] = 'invalid_created:'.$createdRaw; |
| 215 | continue; |
| 216 | } |
| 217 | if ($cdt >= $iniTs && $cdt <= $endTs) { |
| 218 | $ticketProductsInPeriod[$objid] = true; |
| 219 | } else { |
| 220 | $ticketProductsSkipped[$objid] = 'created='.$createdRaw.' ts='.$cdt; |
| 221 | } |
| 222 | } |
| 223 | $logger->info($logPrefix.' ticketproducts in_period='.count($ticketProductsInPeriod).' skipped='.count($ticketProductsSkipped)); |
| 224 | if (count($ticketProductsInPeriod) > 0) { |
| 225 | $logger->debug($logPrefix.' ticketproducts kept objids='.implode(',', array_keys($ticketProductsInPeriod))); |
| 226 | } |
| 227 | if (count($ticketProductsSkipped) > 0) { |
| 228 | $logger->debug($logPrefix.' ticketproducts skipped detail='.json_encode($ticketProductsSkipped, JSON_UNESCAPED_UNICODE)); |
| 229 | } |
| 230 | foreach ($results as &$result) { |
| 231 | $table = $result['table'] ?? ''; |
| 232 | if ($table === 'TICKETPRODUCT') { |
| 233 | if (!isset($ticketProductsInPeriod[(int) $result['objid']])) { |
| 234 | continue; |
| 235 | } |
| 236 | } |
| 237 | $updates[] = &$result; |
| 238 | } |
| 239 | unset($result); |
| 240 | $logger->info($logPrefix.' updates after filter rows='.count($updates).' by_table='.json_encode($countByTable($updates), JSON_UNESCAPED_UNICODE)); |
| 241 | } |
| 242 | |
| 243 | $result = [ |
| 244 | "errorcode" => $retval, |
| 245 | "updates" => \success($retval) ? $updates : null, |
| 246 | ]; |
| 247 | $logger->info($logPrefix.' response errorcode='.$retval.' updates_count='.(\success($retval) ? count($updates) : 0)); |
| 248 | |
| 249 | return ['output' => json_encode($result), 'contentType' => 'application/json; charset=utf-8']; |
| 250 | }; |