1 | <?php |
---|
2 | // MultiMag v0.2 - Complex sales system |
---|
3 | // |
---|
4 | // Copyright (C) 2005-2018, BlackLight, TND Team, http://tndproject.org |
---|
5 | // |
---|
6 | // This program is free software: you can redistribute it and/or modify |
---|
7 | // it under the terms of the GNU Affero General Public License as |
---|
8 | // published by the Free Software Foundation, either version 3 of the |
---|
9 | // License, or (at your option) any later version. |
---|
10 | // |
---|
11 | // This program is distributed in the hope that it will be useful, |
---|
12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
14 | // GNU Affero General Public License for more details. |
---|
15 | // |
---|
16 | // You should have received a copy of the GNU Affero General Public License |
---|
17 | // along with this program. If not, see <http://www.gnu.org/licenses/>. |
---|
18 | // |
---|
19 | |
---|
20 | include_once($CONFIG['site']['location'] . "/include/doc.core.php"); |
---|
21 | |
---|
22 | /// Базовый класс для всех документов системы. Содержит основные методы для работы с документами. |
---|
23 | class doc_Nulltype extends \document { |
---|
24 | |
---|
25 | protected $doc_type; ///< ID типа документа |
---|
26 | protected $sklad_editor_enable; ///< Разрешить отображение редактора склада |
---|
27 | // Значение следующих полей: +1 - увеличивает, -1 - уменьшает, 0 - не влияет |
---|
28 | // Документы перемещений должны иметь 0 в соответствующих полях ! |
---|
29 | protected $bank_modify; ///< Изменяет ли общие средства в банке |
---|
30 | protected $kassa_modify; ///< Изменяет ли общие средства в кассе |
---|
31 | protected $header_fields; ///< Поля заголовка документа, доступные через форму редактирования |
---|
32 | protected $doc_data; ///< Основные данные документа |
---|
33 | protected $dop_data; ///< Дополнительные данные документа |
---|
34 | protected $firm_vars; ///< Информация с данными о фирме |
---|
35 | protected $child_docs = array(); ///< Информация о документах-потомках |
---|
36 | protected $allow_neg_cnt; ///< Разрешить отрицательное количество товара |
---|
37 | |
---|
38 | public function __construct($doc = 0) { |
---|
39 | $this->id = (int) $doc; |
---|
40 | $this->doc_type = 0; |
---|
41 | $this->typename = ''; |
---|
42 | $this->viewname = 'Неопределенный документ'; |
---|
43 | $this->sklad_editor_enable = false; |
---|
44 | $this->bank_modify = 0; |
---|
45 | $this->kassa_modify = 0; |
---|
46 | $this->header_fields = ''; |
---|
47 | $this->get_docdata(); |
---|
48 | } |
---|
49 | |
---|
50 | public function isSkladEditorEnable() { |
---|
51 | return $this->sklad_editor_enable; |
---|
52 | } |
---|
53 | |
---|
54 | public function getFirmVarsA() { |
---|
55 | return $this->firm_vars; |
---|
56 | } |
---|
57 | |
---|
58 | /// Шаблон метода для инициализации дополнительных данных документа |
---|
59 | protected function initDefDopData() { |
---|
60 | |
---|
61 | } |
---|
62 | |
---|
63 | public function getExtControls() { |
---|
64 | return null; |
---|
65 | } |
---|
66 | |
---|
67 | /// Зафиксировать цену документа, если она установлена в *авто*. Выполняется при проведении некоторых типов документов. |
---|
68 | protected function fixPrice() { |
---|
69 | if (!$this->dop_data['cena']) { |
---|
70 | $pc = PriceCalc::getInstance(); |
---|
71 | $pc->setFirmId($this->doc_data['firm_id']); |
---|
72 | $pc->setOrderSum($this->doc_data['sum']); |
---|
73 | $pc->setAgentId($this->doc_data['agent']); |
---|
74 | $pc->setUserId($this->doc_data['user']); |
---|
75 | if (isset($this->dop_data['ishop'])) { |
---|
76 | $pc->setFromSiteFlag($this->dop_data['ishop']); |
---|
77 | } |
---|
78 | $price_id = $pc->getCurrentPriceID(); |
---|
79 | $this->setDopData('cena', $price_id); |
---|
80 | } |
---|
81 | } |
---|
82 | |
---|
83 | /// Создать документ с заданными данными |
---|
84 | public function create($doc_data, $from = 0) { |
---|
85 | global $db; |
---|
86 | \acl::accessGuard('doc.' . $this->typename, \acl::CREATE); |
---|
87 | \acl::accessGuard([ 'firm.global', 'firm.' . $doc_data['firm_id']], \acl::CREATE); |
---|
88 | $date = time(); |
---|
89 | $doc_data['altnum'] = $this->getNextAltNum($this->doc_type, $doc_data['subtype'], date("Y-m-d", $doc_data['date']), $doc_data['firm_id']); |
---|
90 | $doc_data['created'] = date("Y-m-d H:i:s"); |
---|
91 | $res = $db->query("SHOW COLUMNS FROM `doc_list`"); |
---|
92 | $col_array = array(); |
---|
93 | while ($nxt = $res->fetch_row()) { |
---|
94 | $col_array[$nxt[0]] = $nxt[0]; |
---|
95 | } |
---|
96 | // Эти поля копировать не нужно |
---|
97 | unset($col_array['id'], $col_array['date'], $col_array['type'], $col_array['user'], $col_array['ok']); |
---|
98 | |
---|
99 | $data = array_intersect_key($doc_data, $col_array); |
---|
100 | $data['date'] = $date; |
---|
101 | $data['type'] = $this->doc_type; |
---|
102 | $data['user'] = $_SESSION['uid']; |
---|
103 | |
---|
104 | $this->id = $db->insertA('doc_list', $data); |
---|
105 | if ($from) { |
---|
106 | $data['from'] = $from; |
---|
107 | } |
---|
108 | $this->writeLogArray("CREATE", $data); |
---|
109 | unset($this->doc_data); |
---|
110 | unset($this->dop_data); |
---|
111 | $this->get_docdata(); |
---|
112 | return $this->id; |
---|
113 | } |
---|
114 | |
---|
115 | /** Получить ID корневого документа в дереве иерархии |
---|
116 | * |
---|
117 | * @param bool $no_exception Не бросать исключение при ошибке иерархии |
---|
118 | * @return int ID корневого документа |
---|
119 | * @throws Exception Бросает исключение при обнаружении петли с участием текущего документа |
---|
120 | */ |
---|
121 | public function getRootDocumentId($no_exception = false) { |
---|
122 | global $db; |
---|
123 | if ($this->doc_data['p_doc'] == 0) { |
---|
124 | return $this->id; |
---|
125 | } |
---|
126 | |
---|
127 | $docmem = array(); |
---|
128 | $doc = $this->doc_data['p_doc']; |
---|
129 | |
---|
130 | while ($doc) { |
---|
131 | if (in_array($doc, $docmem)) { |
---|
132 | if ($no_exception) { |
---|
133 | return -1; |
---|
134 | } else { |
---|
135 | throw new Exception('Обнаружена петля в иерархии документов!'); |
---|
136 | } |
---|
137 | } |
---|
138 | $res = $db->query("SELECT `p_doc` FROM `doc_list` WHERE `id`='$doc' AND `p_doc`>'0' AND `p_doc` IS NOT NULL"); |
---|
139 | if (!$res->num_rows) { |
---|
140 | return $doc; |
---|
141 | } |
---|
142 | list($pdoc) = $res->fetch_row(); |
---|
143 | if (!$pdoc) { |
---|
144 | return $doc; |
---|
145 | } |
---|
146 | $docmem[] = $doc; |
---|
147 | $doc = $pdoc; |
---|
148 | } |
---|
149 | return $doc; |
---|
150 | } |
---|
151 | |
---|
152 | public function getSubtreeDocuments($doc) { |
---|
153 | global $db; |
---|
154 | settype($doc, 'int'); |
---|
155 | $ret = array(); |
---|
156 | $sql = "SELECT `doc_list`.`id`, `doc_list`.`ok`, `doc_list`.`date`, `doc_list`.`altnum`, `doc_list`.`subtype`, `doc_list`.`sum`, `doc_types`.`name`, |
---|
157 | `doc_list`.`type`, `doc_list`.`firm_id` |
---|
158 | , `doc_agent`.`name` AS `agent_name` |
---|
159 | FROM `doc_list` |
---|
160 | LEFT JOIN `doc_agent` ON `doc_list`.`agent`=`doc_agent`.`id` |
---|
161 | LEFT JOIN `doc_types` ON `doc_types`.`id`=`doc_list`.`type` |
---|
162 | WHERE `doc_list`.`p_doc`='$doc' |
---|
163 | ORDER by `doc_list`.`date` DESC"; |
---|
164 | $res = $db->query($sql); |
---|
165 | $i = 1; |
---|
166 | while ($line = $res->fetch_assoc()) { |
---|
167 | $line['date'] = date("Y.m.d H:i:s", $line['date']); |
---|
168 | $line['childs'] = $this->getSubtreeDocuments($line['id']); |
---|
169 | $ret[] = $line; |
---|
170 | } |
---|
171 | return $ret; |
---|
172 | } |
---|
173 | |
---|
174 | protected function getDocumentSubtreeElementHTML($item, $last = true) { |
---|
175 | $ret = ''; |
---|
176 | |
---|
177 | $ok_status = $item['ok'] ? 'Проведённый' : 'Непроведённый'; |
---|
178 | $r = ($last) ? " IsLast" : ''; |
---|
179 | $ret .= "<li class='Node ExpandLeaf $r'><div class='Expand'></div><div class='Content'>"; |
---|
180 | if (!\acl::testAccess([ 'firm.global', 'firm.' . $item['firm_id']], \acl::VIEW) && $item['firm_id'] > 0) { |
---|
181 | if ($item['id'] == $this->id) { |
---|
182 | $ret .= "<b>"; |
---|
183 | } |
---|
184 | $ret .= "Неизвестный документ N {$item['altnum']}{$item['subtype']} от {$item['date']}." |
---|
185 | . " Агент: {$item['agent_name']}"; |
---|
186 | if ($item['id'] == $this->id) { |
---|
187 | $ret .= "</b>"; |
---|
188 | } |
---|
189 | } else { |
---|
190 | if ($item['id'] == $this->id) { |
---|
191 | $ret .= "<b>"; |
---|
192 | } |
---|
193 | $ret .= "<a href='doc.php?mode=body&doc={$item['id']}'>$ok_status {$item['name']}</a> N {$item['altnum']}{$item['subtype']} от {$item['date']}." |
---|
194 | . " Агент: {$item['agent_name']}, на сумму {$item['sum']}"; |
---|
195 | if ($item['id'] == $this->id) { |
---|
196 | $ret .= "</b>"; |
---|
197 | } |
---|
198 | } |
---|
199 | $ret .= "</li>"; |
---|
200 | return $ret; |
---|
201 | } |
---|
202 | |
---|
203 | protected function getDocumentSubtreeHTML($tree) { |
---|
204 | $ret = ''; |
---|
205 | $cnt = count($tree); |
---|
206 | foreach ($tree as $i => $item) { |
---|
207 | $ret .= $this->getDocumentSubtreeElementHTML($item, $i >= $cnt); |
---|
208 | $ret .= "<ul class='Container'>"; |
---|
209 | $ret .= $this->getDocumentSubtreeHTML($item['childs']); |
---|
210 | $ret .= "</ul></div></li>"; |
---|
211 | } |
---|
212 | return $ret; |
---|
213 | } |
---|
214 | |
---|
215 | public function viewDocumentTree() { |
---|
216 | global $tmpl; |
---|
217 | \acl::accessGuard('doc.' . $this->typename, \acl::VIEW); |
---|
218 | if ($this->doc_data['firm_id'] > 0) { |
---|
219 | \acl::accessGuard([ 'firm.global', 'firm.' . $this->doc_data['firm_id']], \acl::VIEW); |
---|
220 | } |
---|
221 | $root_doc_id = $this->getRootDocumentId(); |
---|
222 | $tmpl->addContent("<h1>Структура для {$this->id} с $root_doc_id </h1>"); |
---|
223 | $root_doc = \document::getInstanceFromDb($root_doc_id); |
---|
224 | $item = $root_doc->getDocDataA(); |
---|
225 | $item['name'] = $root_doc->getViewName(); |
---|
226 | $tree = $this->getSubtreeDocuments($root_doc_id); |
---|
227 | $tmpl->addContent("<ul class='Container'>"); |
---|
228 | $tmpl->addContent($this->getDocumentSubtreeElementHTML($item)); |
---|
229 | $tmpl->addContent("<ul class='Container'>"); |
---|
230 | $tmpl->addContent($this->getDocumentSubtreeHTML($tree)); |
---|
231 | $tmpl->addContent("</ul>"); |
---|
232 | $tmpl->addContent("</ul>"); |
---|
233 | } |
---|
234 | |
---|
235 | /// Создать документ на основе данных другого документа |
---|
236 | public function createFrom($doc_obj) { |
---|
237 | $doc_data = $doc_obj->doc_data; |
---|
238 | $doc_data['p_doc'] = $doc_obj->id; |
---|
239 | $this->create($doc_data, $doc_obj->id); |
---|
240 | |
---|
241 | return $this->id; |
---|
242 | } |
---|
243 | |
---|
244 | /// Создать документ с товарными остатками на основе другого документа |
---|
245 | public function createFromP($doc_obj) { |
---|
246 | global $db; |
---|
247 | $doc_data = $doc_obj->doc_data; |
---|
248 | $doc_data['p_doc'] = $doc_obj->id; |
---|
249 | $this->create($doc_data, $doc_obj->id); |
---|
250 | if ($this->sklad_editor_enable) { |
---|
251 | $res = $db->query("SELECT `tovar`, `cnt`, `cost`, `page`, `comm` FROM `doc_list_pos` WHERE `doc`='{$doc_obj->id}' ORDER BY `doc_list_pos`.`id`"); |
---|
252 | while ($line = $res->fetch_assoc()) { |
---|
253 | $line['doc'] = $this->id; |
---|
254 | unset($line['id']); |
---|
255 | $db->insertA('doc_list_pos', $line); |
---|
256 | } |
---|
257 | } |
---|
258 | return $this->id; |
---|
259 | } |
---|
260 | |
---|
261 | /// Создать несвязанный документ с товарными остатками из другого документа |
---|
262 | public function createParent($doc_obj) { |
---|
263 | global $db; |
---|
264 | $doc_data = $doc_obj->doc_data; |
---|
265 | $doc_data['p_doc'] = 0; |
---|
266 | $this->create($doc_data); |
---|
267 | if ($this->sklad_editor_enable) { |
---|
268 | $res = $db->query("SELECT `tovar`, `cnt`, `cost`, `page`, `comm` FROM `doc_list_pos` WHERE `doc`='{$doc_obj->id}' ORDER BY `doc_list_pos`.`id`"); |
---|
269 | while ($line = $res->fetch_assoc()) { |
---|
270 | $line['doc'] = $this->id; |
---|
271 | unset($line['id']); |
---|
272 | $db->insertA('doc_list_pos', $line); |
---|
273 | } |
---|
274 | } |
---|
275 | unset($this->doc_data); |
---|
276 | $this->get_docdata(); |
---|
277 | return $this->id; |
---|
278 | } |
---|
279 | |
---|
280 | /// Создать документ с товарными остатками на основе другого документа |
---|
281 | /// В новый документ войдут только те наименования, которых нет в других подчинённых документах |
---|
282 | public function createFromPDiff($doc_obj) { |
---|
283 | global $db; |
---|
284 | $doc_data = $doc_obj->doc_data; |
---|
285 | $doc_data['p_doc'] = $doc_obj->id; |
---|
286 | if ($this->sklad_editor_enable) { |
---|
287 | $res = $db->query("SELECT `id` FROM `doc_list` WHERE `p_doc`='{$doc_obj->id}' AND `type`='{$this->doc_type}'"); |
---|
288 | $child_count = $res->num_rows; |
---|
289 | } |
---|
290 | $this->create($doc_data); |
---|
291 | if ($this->sklad_editor_enable) { |
---|
292 | if ($child_count < 1) { |
---|
293 | $res = $db->query("SELECT `tovar`, `cnt`, `cost`, `page`, `comm` FROM `doc_list_pos` WHERE `doc`='{$doc_obj->id}' ORDER BY `doc_list_pos`.`id`"); |
---|
294 | while ($line = $res->fetch_assoc()) { |
---|
295 | $line['doc'] = $this->id; |
---|
296 | unset($line['id']); |
---|
297 | $db->insertA('doc_list_pos', $line); |
---|
298 | } |
---|
299 | } else { |
---|
300 | $res = $db->query("SELECT `a`.`tovar`, `a`.`cnt`, `a`.`comm`, `a`.`cost`, |
---|
301 | ( SELECT SUM(`b`.`cnt`) FROM `doc_list_pos` AS `b` |
---|
302 | INNER JOIN `doc_list` ON `b`.`doc`=`doc_list`.`id` AND `doc_list`.`p_doc`='{$doc_obj->id}' AND `doc_list`.`mark_del`='0' |
---|
303 | WHERE `b`.`tovar`=`a`.`tovar` ) AS `doc_cnt`, `a`.`page` |
---|
304 | FROM `doc_list_pos` AS `a` |
---|
305 | WHERE `a`.`doc`='{$doc_obj->id}' |
---|
306 | ORDER BY `a`.`id`"); |
---|
307 | while ($line = $res->fetch_assoc()) { |
---|
308 | if ($line['doc_cnt'] < $line['cnt']) { |
---|
309 | $line['cnt']-=$line['doc_cnt']; |
---|
310 | unset($line['doc_cnt']); |
---|
311 | $line['doc'] = $this->id; |
---|
312 | unset($line['id']); |
---|
313 | $db->insertA('doc_list_pos', $line); |
---|
314 | } |
---|
315 | } |
---|
316 | } |
---|
317 | $this->recalcSum(); |
---|
318 | } |
---|
319 | return $this->id; |
---|
320 | } |
---|
321 | |
---|
322 | /// Пересчитать и вернуть сумму документа, исходя из товаров в нём. Работает только для документов, в которых могут быть товары. |
---|
323 | /// Для безтоварных документов просто вернёт сумму. |
---|
324 | /// TODO: функция устарела. Перейти на использование DocPosEditor::updateDocSum() |
---|
325 | public function recalcSum() { |
---|
326 | global $db; |
---|
327 | if (!$this->id) |
---|
328 | return 0; |
---|
329 | if (!$this->sklad_editor_enable) |
---|
330 | return $this->doc_data['sum']; |
---|
331 | $old_sum = $this->doc_data['sum']; |
---|
332 | $sum = 0; |
---|
333 | $res = $db->query("SELECT `cnt`, `cost` FROM `doc_list_pos` WHERE `doc`='{$this->id}' AND `page`='0'"); |
---|
334 | while ($nxt = $res->fetch_row()) |
---|
335 | $sum+=$nxt[0] * $nxt[1]; |
---|
336 | $res->free(); |
---|
337 | if (round($sum, 2) != round($old_sum, 2)) |
---|
338 | $this->setDocData('sum', $sum); |
---|
339 | return $sum; |
---|
340 | } |
---|
341 | |
---|
342 | /// Получить объект документа заявки для текущей цепочки документов |
---|
343 | /// @return Объект doc_Zayavka, или false если не найден. Может быть текущим документом. |
---|
344 | public function getZDoc() { |
---|
345 | global $db; |
---|
346 | if ($this->doc_type == 3) { |
---|
347 | return $this; |
---|
348 | } |
---|
349 | $pdoc = $this->doc_data['p_doc']; |
---|
350 | while ($pdoc) { |
---|
351 | $res = $db->query("SELECT `id`, `type`, `p_doc` FROM `doc_list` WHERE `id`='$pdoc'"); |
---|
352 | if (!$res->num_rows) { |
---|
353 | throw new Exception("Документ не найден"); |
---|
354 | } |
---|
355 | list($doc_id, $pdoc_type, $pdoc_id) = $res->fetch_row(); |
---|
356 | if ($pdoc_type == 3) { |
---|
357 | return new doc_Zayavka($doc_id); |
---|
358 | } |
---|
359 | $pdoc = $pdoc_id; |
---|
360 | } |
---|
361 | return false; |
---|
362 | } |
---|
363 | |
---|
364 | /// Послать в связанный заказ событие с заданным типом. |
---|
365 | /// Полное название события будет doc:{$docname}:{$event_type} |
---|
366 | /// @param event_type Название события |
---|
367 | /// TODO: зависимость от дочернего класса выглядит некорректной |
---|
368 | public function sentZEvent($event_type) { |
---|
369 | global $db; |
---|
370 | $event_name = "doc:{$this->typename}:$event_type"; |
---|
371 | $zdoc = $this->getZDoc(); |
---|
372 | if ($zdoc) { |
---|
373 | return $zdoc->dispatchZEvent($event_name, $this); |
---|
374 | } |
---|
375 | return false; |
---|
376 | } |
---|
377 | |
---|
378 | /// Отправить оповещение по всем доступным каналам связи с клиентом |
---|
379 | function sendNotify($text) { |
---|
380 | return |
---|
381 | $this->sendEmailNotify($text) || |
---|
382 | $this->sendSMSNotify($text) || |
---|
383 | $this->sendXMPPNotify($text); |
---|
384 | } |
---|
385 | |
---|
386 | /// Отправить SMS с заданным текстом заказчику на первый из подходящих номеров |
---|
387 | /// @param text текст отправляемого сообщения |
---|
388 | function sendSMSNotify($text) { |
---|
389 | global $CONFIG, $db; |
---|
390 | if (!isset($CONFIG['doc']['notify_sms'])) { |
---|
391 | return false; |
---|
392 | } |
---|
393 | if (!$CONFIG['doc']['notify_sms']) { |
---|
394 | return false; |
---|
395 | } |
---|
396 | if (isset($this->dop_data['buyer_phone'])) { |
---|
397 | if (preg_match('/^\+79\d{9}$/', $this->dop_data['buyer_phone'])) { |
---|
398 | $smsphone = $this->dop_data['buyer_phone']; |
---|
399 | } |
---|
400 | } |
---|
401 | if ($this->doc_data['agent'] > 1 && !$smsphone) { |
---|
402 | $agent = new \models\agent($this->doc_data['agent']); |
---|
403 | $smsphone = $agent->getSMSPhone(); |
---|
404 | } |
---|
405 | if (preg_match('/^\+79\d{9}$/', $smsphone)) { |
---|
406 | require_once('include/sendsms.php'); |
---|
407 | $sender = new SMSSender(); |
---|
408 | $sender->setNumber($smsphone); |
---|
409 | $sender->setContent($text); |
---|
410 | $sender->send(); |
---|
411 | if (@$CONFIG['doc']['notify_debug']) { |
---|
412 | $this->writeLogArray("NOTIFY SMS", ['number' => $smsphone, 'text' => $text]); |
---|
413 | } |
---|
414 | return true; |
---|
415 | } |
---|
416 | return false; |
---|
417 | } |
---|
418 | |
---|
419 | /// Отправить email с заданным текстом заказчику на все доступные адреса |
---|
420 | /// @param text текст отправляемого сообщения |
---|
421 | function sendEmailNotify($text, $subject = null) { |
---|
422 | global $CONFIG, $db; |
---|
423 | $pref = \pref::getInstance(); |
---|
424 | if (!isset($CONFIG['doc']['notify_email'])) { |
---|
425 | return false; |
---|
426 | } |
---|
427 | if (!$CONFIG['doc']['notify_email']) { |
---|
428 | return false; |
---|
429 | } |
---|
430 | $emails = array(); |
---|
431 | if (isset($this->dop_data['buyer_email'])) { |
---|
432 | if ($this->dop_data['buyer_email']) { |
---|
433 | $emails[$this->dop_data['buyer_email']] = $this->dop_data['buyer_email']; |
---|
434 | } |
---|
435 | } |
---|
436 | if ($this->doc_data['agent'] > 1) { |
---|
437 | $agent = new \models\agent($this->doc_data['agent']); |
---|
438 | $contacts = $agent->contacts; |
---|
439 | foreach ($contacts as $line) { |
---|
440 | if ($line['type'] == 'email') { |
---|
441 | $emails[$line['value']] = $line['value']; |
---|
442 | } |
---|
443 | } |
---|
444 | } |
---|
445 | if (count($emails) > 0) { |
---|
446 | foreach ($emails as $email) { |
---|
447 | $user_msg = "Уважаемый клиент!\n" . $text; |
---|
448 | if (!$subject) { |
---|
449 | $subject = "Документ N {$this->id} на {$pref->site_name}"; |
---|
450 | } |
---|
451 | mailto($email, $subject, $user_msg); |
---|
452 | if (@$CONFIG['doc']['notify_debug']) { |
---|
453 | $this->writeLogArray("NOTIFY Email", ['email' => $email, 'text' => $user_msg]); |
---|
454 | } |
---|
455 | } |
---|
456 | return true; |
---|
457 | } |
---|
458 | return false; |
---|
459 | } |
---|
460 | |
---|
461 | /// Отправить сообщение по XMPP с заданным текстом заказчику на все доступные адреса |
---|
462 | /// @param text текст отправляемого сообщения |
---|
463 | function sendXMPPNotify($text) { |
---|
464 | global $CONFIG, $db; |
---|
465 | if (!isset($CONFIG['doc']['notify_xmpp'])) { |
---|
466 | return false; |
---|
467 | } |
---|
468 | if (!$CONFIG['doc']['notify_xmpp']) { |
---|
469 | return false; |
---|
470 | } |
---|
471 | $addresses = array(); |
---|
472 | if ($this->doc_data['agent'] > 1) { |
---|
473 | $agent = new \models\agent($this->doc_data['agent']); |
---|
474 | $contacts = $agent->contacts; |
---|
475 | foreach ($contacts as $line) { |
---|
476 | if ($line['type'] == 'jid' || $line['type'] == 'xmpp') { |
---|
477 | $addresses[$line['value']] = $line['value']; |
---|
478 | } |
---|
479 | } |
---|
480 | } |
---|
481 | if (count($addresses) > 0) { |
---|
482 | require_once($CONFIG['location'] . '/common/XMPPHP/XMPP.php'); |
---|
483 | $xmppclient = new \XMPPHP\XMPP($CONFIG['xmpp']['host'], $CONFIG['xmpp']['port'], $CONFIG['xmpp']['login'], $CONFIG['xmpp']['pass'], 'MultiMag r' . MULTIMAG_REV); |
---|
484 | $xmppclient->connect(); |
---|
485 | $xmppclient->processUntil('session_start'); |
---|
486 | $xmppclient->presence(); |
---|
487 | foreach ($addresses as $addr) { |
---|
488 | $user_msg = $text; |
---|
489 | $xmppclient->message($addr, $user_msg); |
---|
490 | if (@$CONFIG['doc']['notify_debug']) { |
---|
491 | $this->writeLogArray("NOTIFY xmpp", ['jid' => $addr, 'text' => $user_msg]); |
---|
492 | } |
---|
493 | } |
---|
494 | $xmppclient->disconnect(); |
---|
495 | return true; |
---|
496 | } |
---|
497 | return false; |
---|
498 | } |
---|
499 | |
---|
500 | /// отобразить заголовок документа |
---|
501 | public function head() { |
---|
502 | global $tmpl; |
---|
503 | if ($this->doc_type == 0) |
---|
504 | throw new Exception("Невозможно создать документ без типа!"); |
---|
505 | else { |
---|
506 | $tmpl->setTitle($this->viewname . ' N' . $this->id); |
---|
507 | if ($this->typename) |
---|
508 | $object = 'doc_' . $this->typename; |
---|
509 | else |
---|
510 | $object = 'doc'; |
---|
511 | \acl::accessGuard('doc.' . $this->typename, \acl::VIEW); |
---|
512 | if ($this->doc_data['firm_id'] > 0) { |
---|
513 | \acl::accessGuard([ 'firm.global', 'firm.' . $this->doc_data['firm_id']], \acl::VIEW); |
---|
514 | } |
---|
515 | doc_menu($this->getDopButtons()); |
---|
516 | $this->drawHeadformStart(); |
---|
517 | $fields = explode(' ', $this->header_fields); |
---|
518 | foreach ($fields as $f) { |
---|
519 | switch ($f) { |
---|
520 | case 'agent': $this->DrawAgentField(); |
---|
521 | break; |
---|
522 | case 'sklad': $this->DrawSkladField(); |
---|
523 | break; |
---|
524 | case 'kassa': $this->drawKassaField(); |
---|
525 | break; |
---|
526 | case 'bank': $this->drawBankField(); |
---|
527 | break; |
---|
528 | case 'cena': $this->drawPriceField(); |
---|
529 | break; |
---|
530 | case 'sum': $this->drawSumField(); |
---|
531 | break; |
---|
532 | case 'separator': $tmpl->addContent("<hr>"); |
---|
533 | break; |
---|
534 | } |
---|
535 | } |
---|
536 | if (method_exists($this, 'DopHead')) |
---|
537 | $this->DopHead(); |
---|
538 | |
---|
539 | $this->DrawHeadformEnd(); |
---|
540 | } |
---|
541 | } |
---|
542 | |
---|
543 | protected function try_head_save() { |
---|
544 | $write_doc_data = array( |
---|
545 | 'date' => @strtotime(request('datetime')), |
---|
546 | 'firm_id' => rcvint('firm'), |
---|
547 | 'comment' => request('comment'), |
---|
548 | 'altnum' => rcvint('altnum'), |
---|
549 | 'subtype' => request('subtype'), |
---|
550 | ); |
---|
551 | $write_dop_data = array(); |
---|
552 | if (!$write_doc_data['altnum']) { |
---|
553 | $write_doc_data['altnum'] = $this->getNextAltNum($this->doc_type, $write_doc_data['subtype'] |
---|
554 | , date("Y-m-d", $write_doc_data['date']), $write_doc_data['firm_id']); |
---|
555 | } |
---|
556 | if (!$this->id) { |
---|
557 | $write_doc_data['user'] = intval($_SESSION['uid']); |
---|
558 | $write_doc_data['type'] = $this->doc_type; |
---|
559 | } elseif (@$this->doc_data['ok']) { |
---|
560 | throw new \Exception("Операция не допускается для проведённого документа!"); |
---|
561 | } else if (@$this->doc_data['mark_del']) { |
---|
562 | throw new \Exception("Операция не допускается для документа, отмеченного для удаления!"); |
---|
563 | } |
---|
564 | $fields = explode(' ', $this->header_fields); |
---|
565 | foreach ($fields as $f) { |
---|
566 | switch ($f) { |
---|
567 | case 'cena': |
---|
568 | case 'price': |
---|
569 | $write_dop_data['cena'] = rcvint('cena'); |
---|
570 | $write_doc_data['nds'] = rcvint('nds'); |
---|
571 | break; |
---|
572 | case 'agent': |
---|
573 | $write_doc_data['agent'] = rcvint('agent'); |
---|
574 | if (!$write_doc_data['agent']) { |
---|
575 | $pref = \pref::getInstance(); |
---|
576 | $write_doc_data['agent'] = $pref->getSitePref('default_agent_id'); |
---|
577 | } |
---|
578 | $write_doc_data['contract'] = rcvint('contract'); |
---|
579 | break; |
---|
580 | case 'separator': |
---|
581 | break; |
---|
582 | case 'sum': |
---|
583 | $write_doc_data['sum'] = rcvrounded('sum'); |
---|
584 | break; |
---|
585 | default: |
---|
586 | $write_doc_data[$f] = rcvint($f); |
---|
587 | break; |
---|
588 | } |
---|
589 | } |
---|
590 | if ($this->id) { |
---|
591 | \acl::accessGuard('doc.' . $this->typename, \acl::UPDATE); |
---|
592 | \acl::accessGuard([ 'firm.global', 'firm.' . $this->doc_data['firm_id']], \acl::UPDATE); |
---|
593 | } else { |
---|
594 | \acl::accessGuard('doc.' . $this->typename, \acl::CREATE); |
---|
595 | if ($this->doc_data['firm_id'] > 0) { |
---|
596 | \acl::accessGuard([ 'firm.global', 'firm.' . $this->doc_data['firm_id']], \acl::CREATE); |
---|
597 | } |
---|
598 | } |
---|
599 | $this->setDocDataA($write_doc_data); |
---|
600 | if (count($write_dop_data)) { |
---|
601 | $this->setDopDataA($write_dop_data); |
---|
602 | } |
---|
603 | if (method_exists($this, 'DopSave')) { |
---|
604 | $this->DopSave(); |
---|
605 | } |
---|
606 | } |
---|
607 | |
---|
608 | /// Применить изменения редактирования заголовка |
---|
609 | public function head_submit() { |
---|
610 | $this->try_head_save(); |
---|
611 | redirect("/doc.php?mode=body&doc={$this->id}"); |
---|
612 | return $this->id; |
---|
613 | } |
---|
614 | |
---|
615 | /// Сохранение заголовка документа и возврат результата в json формате |
---|
616 | public function json_head_submit() { |
---|
617 | global $tmpl; |
---|
618 | $tmpl->ajax = 1; |
---|
619 | try { |
---|
620 | $this->try_head_save(); |
---|
621 | if ($this->doc_data['agent']) { |
---|
622 | $b = agentCalcDebt($this->doc_data['agent']); |
---|
623 | } else { |
---|
624 | $b = 0; |
---|
625 | } |
---|
626 | $json_content = json_encode(['response' => 'ok', 'agent_balance' => $b], JSON_UNESCAPED_UNICODE); |
---|
627 | $tmpl->setContent($json_content); |
---|
628 | } catch (mysqli_sql_exception $e) { |
---|
629 | $id = writeLogException($e); |
---|
630 | $ret_data = array('response' => 'err', |
---|
631 | 'text' => "Ошибка в базе данных! Порядковый номер ошибки: $id. Сообщение об ошибке занесено в журнал."); |
---|
632 | $tmpl->setContent(json_encode($ret_data, JSON_UNESCAPED_UNICODE)); |
---|
633 | } catch (Exception $e) { |
---|
634 | $json_content = json_encode(['response' => 'err', 'text' => $e->getMessage()], JSON_UNESCAPED_UNICODE); |
---|
635 | $tmpl->setContent($json_content); |
---|
636 | } |
---|
637 | } |
---|
638 | |
---|
639 | /// Редактирование тела докумнета |
---|
640 | public function body() { |
---|
641 | global $tmpl, $db; |
---|
642 | \acl::accessGuard('doc.' . $this->typename, \acl::VIEW); |
---|
643 | if ($this->doc_data['firm_id'] > 0) { |
---|
644 | \acl::accessGuard([ 'firm.global', 'firm.' . $this->doc_data['firm_id']], \acl::VIEW); |
---|
645 | } |
---|
646 | $this->extendedViewAclCheck(); |
---|
647 | $tmpl->setTitle($this->viewname . ' N' . $this->id); |
---|
648 | $dt = date("Y-m-d H:i:s", $this->doc_data['date']); |
---|
649 | doc_menu($this->getDopButtons()); |
---|
650 | $tmpl->addContent("<div id='doc_container'> |
---|
651 | <div id='doc_left_block' class='doc_head'>"); |
---|
652 | $tmpl->addContent("<h1>{$this->viewname} N{$this->id}</h1>"); |
---|
653 | |
---|
654 | $this->drawLHeadformStart(); |
---|
655 | $fields = explode(' ', $this->header_fields); |
---|
656 | foreach ($fields as $f) { |
---|
657 | switch ($f) { |
---|
658 | case 'agent': $this->DrawAgentField(); |
---|
659 | break; |
---|
660 | case 'sklad': $this->DrawSkladField(); |
---|
661 | break; |
---|
662 | case 'kassa': $this->drawKassaField(); |
---|
663 | break; |
---|
664 | case 'bank': $this->drawBankField(); |
---|
665 | break; |
---|
666 | case 'cena': $this->drawPriceField(); |
---|
667 | break; |
---|
668 | case 'sum': $this->drawSumField(); |
---|
669 | break; |
---|
670 | case 'separator': $tmpl->addContent("<hr>"); |
---|
671 | break; |
---|
672 | } |
---|
673 | } |
---|
674 | if (method_exists($this, 'DopHead')) |
---|
675 | $this->DopHead(); |
---|
676 | |
---|
677 | $this->DrawLHeadformEnd(); |
---|
678 | |
---|
679 | |
---|
680 | if($info = $this->getParentInfo()) { |
---|
681 | $str = "<b>Относится к:</b><br>"; |
---|
682 | if($info['ok']) { |
---|
683 | $str.='Проведённый'; |
---|
684 | } |
---|
685 | else { |
---|
686 | $str.='Непроведённый'; |
---|
687 | } |
---|
688 | $str .= " <a href='?mode=body&doc={$info['id']}'>{$info['viewname']} N{$info['altnum']}{$info['subtype']}</a> от {$info['date']}"; |
---|
689 | $tmpl->addContent($str); |
---|
690 | } |
---|
691 | |
---|
692 | $infol = $this->getSubordinatesInfo(); |
---|
693 | if($infol && count($infol)>0) { |
---|
694 | $tmpl->addContent("<br><b>Зависящие документы:</b><br>"); |
---|
695 | foreach($infol as $info) { |
---|
696 | if($info['ok']) { |
---|
697 | $str='Проведённый'; |
---|
698 | } |
---|
699 | else { |
---|
700 | $str='Непроведённый'; |
---|
701 | } |
---|
702 | $str .= " <a href='?mode=body&doc={$info['id']}'>{$info['viewname']} N{$info['altnum']}{$info['subtype']}</a> от {$info['date']}<br>"; |
---|
703 | $tmpl->addContent($str); |
---|
704 | } |
---|
705 | } |
---|
706 | |
---|
707 | $tmpl->addContent("<br><b>Дата создания:</b>: {$this->doc_data['created']}<br>"); |
---|
708 | if ($this->doc_data['ok']) { |
---|
709 | $tmpl->addContent("<b>Дата проведения:</b> " . date("Y-m-d H:i:s", $this->doc_data['ok']) . "<br>"); |
---|
710 | } |
---|
711 | $tmpl->addContent("</div> |
---|
712 | <script type=\"text/javascript\"> |
---|
713 | addEventListener('load',DocHeadInit,false); |
---|
714 | //newDynamicDocHeader('doc_left_block', '{$this->id}'); |
---|
715 | </script>"); |
---|
716 | $tmpl->addContent("<div id='doc_main_block'>"); |
---|
717 | $tmpl->addContent("<img src='/img/i_leftarrow.png' onclick='DocLeftToggle()' id='doc_left_arrow'><br>"); |
---|
718 | |
---|
719 | if (method_exists($this, 'DopBody')) |
---|
720 | $this->DopBody(); |
---|
721 | |
---|
722 | if ($this->sklad_editor_enable) { |
---|
723 | include_once('doc.poseditor.php'); |
---|
724 | $poseditor = new DocPosEditor($this); |
---|
725 | $poseditor->cost_id = $this->dop_data['cena']; |
---|
726 | $poseditor->sklad_id = $this->doc_data['sklad']; |
---|
727 | $poseditor->SetEditable($this->doc_data['ok'] ? 0 : 1); |
---|
728 | $tmpl->addContent($poseditor->Show()); |
---|
729 | } |
---|
730 | |
---|
731 | $tmpl->addContent("<div id='statusblock'></div><br><br></div></div>"); |
---|
732 | } |
---|
733 | |
---|
734 | |
---|
735 | |
---|
736 | /// Выполнение дополнительных проверок доступа для просмотра документа |
---|
737 | public function extendedViewAclCheck() { |
---|
738 | return true; |
---|
739 | } |
---|
740 | |
---|
741 | /// Выполнение дополнительных проверок доступа для проведения документа |
---|
742 | public function extendedApplyAclCheck() { |
---|
743 | return true; |
---|
744 | } |
---|
745 | |
---|
746 | /// Выполнение дополнительных проверок доступа для отмены документа |
---|
747 | public function extendedCancelAclCheck() { |
---|
748 | return true; |
---|
749 | } |
---|
750 | |
---|
751 | /// Провести документ |
---|
752 | public function apply($silent = false) { |
---|
753 | global $db; |
---|
754 | if ($this->doc_data['mark_del']) { |
---|
755 | throw new \Exception("Документ помечен на удаление!"); |
---|
756 | } |
---|
757 | $this->docApply($silent); |
---|
758 | if(!$silent) { |
---|
759 | doc_log("APPLY", '', 'doc', $this->id); |
---|
760 | } |
---|
761 | $db->query("UPDATE `doc_list` SET `err_flag`='0' WHERE `id`='{$this->id}'"); |
---|
762 | } |
---|
763 | |
---|
764 | /// Провести документ и вернуть JSON результат |
---|
765 | public function applyJson() { |
---|
766 | global $db; |
---|
767 | try { |
---|
768 | $d_start = date_day(time()); |
---|
769 | $d_end = $d_start + 60 * 60 * 24 - 1; |
---|
770 | if (!\acl::testAccess('doc.' . $this->typename, \acl::APPLY)) { |
---|
771 | if (!\acl::testAccess('doc.' . $this->typename, \acl::TODAY_APPLY)) { |
---|
772 | throw new AccessException('Не достаточно привилегий для проведения документа'); |
---|
773 | } elseif ($this->doc_data['date'] < $d_start || $this->doc_data['date'] > $d_end) { |
---|
774 | throw new AccessException('Не достаточно привилегий для проведения документа произвольной датой'); |
---|
775 | } |
---|
776 | } |
---|
777 | $this->extendedApplyAclCheck(); |
---|
778 | if ($this->doc_data['mark_del']) { |
---|
779 | throw new Exception("Документ помечен на удаление!"); |
---|
780 | } |
---|
781 | |
---|
782 | $res = $db->query("SELECT `recalc_active` FROM `variables`"); |
---|
783 | if ($res->num_rows) { |
---|
784 | list($lock) = $res->fetch_row(); |
---|
785 | } else { |
---|
786 | $lock = 0; |
---|
787 | } |
---|
788 | if ($lock) { |
---|
789 | throw new Exception("Идёт обслуживание базы данных. Проведение невозможно!"); |
---|
790 | } |
---|
791 | |
---|
792 | $db->startTransaction(); |
---|
793 | $this->DocApply(0); |
---|
794 | $db->query("UPDATE `doc_list` SET `err_flag`='0' WHERE `id`='{$this->id}'"); |
---|
795 | doc_log("APPLY", '', 'doc', $this->id); |
---|
796 | $db->commit(); |
---|
797 | } catch (mysqli_sql_exception $e) { |
---|
798 | $db->rollback(); |
---|
799 | writeLogException($e); |
---|
800 | |
---|
801 | $data = array( |
---|
802 | 'response' => 0, |
---|
803 | 'message' => $e->getMessage(), |
---|
804 | ); |
---|
805 | $json = json_encode($data, JSON_UNESCAPED_UNICODE); |
---|
806 | return $json; |
---|
807 | } catch (Exception $e) { |
---|
808 | $db->rollback(); |
---|
809 | writeLogException($e); |
---|
810 | $data = array( |
---|
811 | 'response' => 0, |
---|
812 | 'message' => $e->getMessage(), |
---|
813 | ); |
---|
814 | $json = json_encode($data, JSON_UNESCAPED_UNICODE); |
---|
815 | return $json; |
---|
816 | } |
---|
817 | |
---|
818 | $data = array( |
---|
819 | 'response' => 1, |
---|
820 | 'message' => "Документ успешно проведён!", |
---|
821 | 'buttons' => $this->getCancelButtons(), |
---|
822 | 'sklad_view' => 'hide', |
---|
823 | 'statusblock' => 'Дата проведения: ' . date("Y-m-d H:i:s"), |
---|
824 | 'poslist' => 'refresh', |
---|
825 | ); |
---|
826 | $json = json_encode($data, JSON_UNESCAPED_UNICODE); |
---|
827 | return $json; |
---|
828 | } |
---|
829 | |
---|
830 | /// Отменить проведение документа |
---|
831 | public function cancel() { |
---|
832 | $this->docCancel(); |
---|
833 | doc_log("CANCEL", '', 'doc', $this->id); |
---|
834 | } |
---|
835 | |
---|
836 | public function cancelJson() { |
---|
837 | global $db; |
---|
838 | $tim = time(); |
---|
839 | $dd = date_day($tim); |
---|
840 | if ($this->typename) { |
---|
841 | $object = 'doc_' . $this->typename; |
---|
842 | } else { |
---|
843 | $object = 'doc'; |
---|
844 | } |
---|
845 | |
---|
846 | try { |
---|
847 | if (!\acl::testAccess('doc.' . $this->typename, \acl::CANCEL)) { |
---|
848 | if ((!\acl::testAccess('doc.' . $this->typename, \acl::TODAY_CANCEL)) || ($dd > $this->doc_data['date'])) { |
---|
849 | throw new \AccessException(); |
---|
850 | } |
---|
851 | } |
---|
852 | $this->extendedCancelAclCheck(); |
---|
853 | |
---|
854 | $res = $db->query("SELECT `recalc_active` FROM `variables`"); |
---|
855 | if ($res->num_rows) { |
---|
856 | list($lock) = $res->fetch_row(); |
---|
857 | } else { |
---|
858 | $lock = 0; |
---|
859 | } |
---|
860 | if ($lock) { |
---|
861 | throw new \Exception("Идёт обслуживание базы данных. Проведение невозможно!"); |
---|
862 | } |
---|
863 | |
---|
864 | $db->startTransaction(); |
---|
865 | $this->get_docdata(); |
---|
866 | $this->DocCancel(); |
---|
867 | $db->query("UPDATE `doc_list` SET `err_flag`='0' WHERE `id`='{$this->id}'"); |
---|
868 | } catch (mysqli_sql_exception $e) { |
---|
869 | $db->rollback(); |
---|
870 | writeLogException($e); |
---|
871 | $json = " { \"response\": \"0\", \"message\": \"" . $e->getMessage() . "\" }"; |
---|
872 | return $json; |
---|
873 | } catch (AccessException $e) { |
---|
874 | $db->rollback(); |
---|
875 | doc_log("CANCEL-DENIED", $e->getMessage(), 'doc', $this->id); |
---|
876 | $json = " { \"response\": \"0\", \"message\": \"Недостаточно привилегий для выполнения операции!<br>" . $e->getMessage() . "<br>Вы можете <a href='#' onclick=\"return petitionMenu(event, '{$this->id}')\">попросить руководителя</a> выполнить отмену этого документа.\" }"; |
---|
877 | return $json; |
---|
878 | } catch (Exception $e) { |
---|
879 | $db->rollback(); |
---|
880 | $msg = ''; |
---|
881 | if (\acl::testAccess('doc.' . $this->typename, \acl::CANCEL_FORCE)) { |
---|
882 | $msg = "<br>Вы можете <a href='/doc.php?mode=forcecancel&doc={$this->id}'>принудительно снять проведение</a>."; |
---|
883 | } |
---|
884 | $json = " { \"response\": \"0\", \"message\": \"" . $e->getMessage() . $msg . "\" }"; |
---|
885 | return $json; |
---|
886 | } |
---|
887 | |
---|
888 | $db->commit(); |
---|
889 | doc_log("CANCEL", '', 'doc', $this->id); |
---|
890 | $json = ' { "response": "1", "message": "Документ успешно отменен!", "buttons": "' . $this->getApplyButtons() . '", "sklad_view": "show", "statusblock": "Документ отменён", "poslist": "refresh" }'; |
---|
891 | return $json; |
---|
892 | } |
---|
893 | |
---|
894 | /// Провести документ |
---|
895 | /// @param silent Не менять отметку проведения |
---|
896 | public function docApply($silent = 0) { |
---|
897 | global $db; |
---|
898 | if ($silent) { |
---|
899 | return; |
---|
900 | } |
---|
901 | if ($this->doc_data['ok']) { |
---|
902 | throw new Exception('Документ уже проведён!'); |
---|
903 | } |
---|
904 | $ok_time = time(); |
---|
905 | $db->update('doc_list', $this->id, 'ok', $ok_time); |
---|
906 | $this->doc_data['ok'] = $ok_time; |
---|
907 | $this->sentZEvent('apply'); |
---|
908 | } |
---|
909 | |
---|
910 | /// отменить проведение документа |
---|
911 | public function docCancel() { |
---|
912 | global $db; |
---|
913 | if (!$this->doc_data['ok']) { |
---|
914 | throw new \Exception('Документ не проведён!'); |
---|
915 | } |
---|
916 | $db->update('doc_list', $this->id, 'ok', 0); |
---|
917 | $this->doc_data['ok'] = 0; |
---|
918 | $this->sentZEvent('cancel'); |
---|
919 | } |
---|
920 | |
---|
921 | /// Отменить проведение, не обращая внимание на структуру подчинённости |
---|
922 | function forceCancel() { |
---|
923 | global $tmpl, $db; |
---|
924 | |
---|
925 | \acl::accessGuard('doc.' . $this->typename, \acl::CANCEL_FORCE); |
---|
926 | if ($this->doc_data['firm_id'] > 0) { |
---|
927 | \acl::accessGuard([ 'firm.global', 'firm.' . $this->doc_data['firm_id']], \acl::CANCEL_FORCE); |
---|
928 | } |
---|
929 | $opt = request('opt'); |
---|
930 | if ($opt == '') { |
---|
931 | $tmpl->addContent("<h2>Внимание! Опасная операция!</h2>Отмена производится простым снятием отметки проведения, без проверки зависимостией, учета структуры подчинённости и изменения значений счётчиков. Вы приниматете на себя все последствия данного действия. Вы точно хотите это сделать?<br> |
---|
932 | <center> |
---|
933 | <a href='/docj_new.php' style='color: #0b0'>Нет</a> | |
---|
934 | <a href='/doc.php?mode=forcecancel&opt=yes&doc={$this->id}' style='color: #f00'>Да</a> |
---|
935 | </center>"); |
---|
936 | } else { |
---|
937 | doc_log("FORCE CANCEL", '', 'doc', $this->id); |
---|
938 | $db->query("UPDATE `doc_list` SET `ok`='0', `err_flag`='1' WHERE `id`='{$this->id}'"); |
---|
939 | $db->query("UPDATE `variables` SET `corrupted`='1'"); |
---|
940 | $tmpl->msg("Всё, сделано.", "err", "Снятие отметки проведения"); |
---|
941 | } |
---|
942 | } |
---|
943 | |
---|
944 | /// Callback функция для сортировки (например, печатных форм) |
---|
945 | static function sortDescriptionCallback($a, $b) { |
---|
946 | return strcmp($a["desc"], $b["desc"]); |
---|
947 | } |
---|
948 | |
---|
949 | /// Получить список доступных печатных форм |
---|
950 | /// @return Массив со списком печатных форм |
---|
951 | protected function getPrintFormList() { |
---|
952 | global $CONFIG; |
---|
953 | $aclFlag = \acl::GET_PRINTDRAFT; |
---|
954 | if ($this->doc_data['ok']) { |
---|
955 | $aclFlag = \acl::GET_PRINTFORM; |
---|
956 | } |
---|
957 | $ret = array(); |
---|
958 | if (isset($this->PDFForms)) { |
---|
959 | if (is_array($this->PDFForms)) { |
---|
960 | foreach ($this->PDFForms as $form) { |
---|
961 | $ret[] = array('name' => 'int:' . $form['name'], 'desc' => $form['desc'], 'mime' => ''); |
---|
962 | } |
---|
963 | } |
---|
964 | } |
---|
965 | $dir = $CONFIG['site']['location'] . '/include/doc/printforms/' . $this->typename . '/'; |
---|
966 | if (is_dir($dir)) { |
---|
967 | $dh = opendir($dir); |
---|
968 | if ($dh) { |
---|
969 | while (($file = readdir($dh)) !== false) { |
---|
970 | if (preg_match('/.php$/', $file)) { |
---|
971 | $cn = explode('.', $file); |
---|
972 | $class_name = '\\doc\\printforms\\' . $this->typename . '\\' . $cn[0]; |
---|
973 | $class = new $class_name; |
---|
974 | $nm = $class->getName(); |
---|
975 | $mime = $class->getMimeType(); |
---|
976 | if (\acl::testAccess("doc.{$this->typename}.{$cn[0]}", $aclFlag)) { |
---|
977 | $ret[] = array('name' => 'ext:' . $cn[0], 'desc' => $nm, 'mime' => $mime); |
---|
978 | } |
---|
979 | } |
---|
980 | } |
---|
981 | closedir($dh); |
---|
982 | } |
---|
983 | } |
---|
984 | usort($ret, array(get_class(), 'sortDescriptionCallback')); |
---|
985 | return $ret; |
---|
986 | } |
---|
987 | |
---|
988 | /// Получить список доступных печатных форм c CSV экспортом |
---|
989 | /// @return Массив со списком печатных форм |
---|
990 | public function getCSVPrintFormList() { |
---|
991 | $ret = $this->getPrintFormList(); |
---|
992 | if ($this->sklad_editor_enable) { |
---|
993 | $ret[] = array('name' => 'csv:export', 'desc' => 'Экспорт в CSV', 'mime' => 'text/csv'); |
---|
994 | } |
---|
995 | return $ret; |
---|
996 | } |
---|
997 | |
---|
998 | /// Проверить, существует ли печатная форма с заданным названием |
---|
999 | /// @return true, если существует, false в ином случае |
---|
1000 | protected function isPrintFormExists($form_name) { |
---|
1001 | $forms = $this->getCSVPrintFormList(); |
---|
1002 | $found = false; |
---|
1003 | foreach ($forms as $form) { |
---|
1004 | if ($form['name'] == $form_name) { |
---|
1005 | $found = true; |
---|
1006 | break; |
---|
1007 | } |
---|
1008 | } |
---|
1009 | return $found; |
---|
1010 | } |
---|
1011 | |
---|
1012 | /// Получить mime тип формы |
---|
1013 | /// @return тип, если форма существует, false в ином случае |
---|
1014 | protected function getPrintFormMime($form_name) { |
---|
1015 | $forms = $this->getCSVPrintFormList(); |
---|
1016 | $found = false; |
---|
1017 | foreach ($forms as $form) { |
---|
1018 | if ($form['name'] == $form_name) { |
---|
1019 | $found = $form['mime']; |
---|
1020 | break; |
---|
1021 | } |
---|
1022 | } |
---|
1023 | return $found; |
---|
1024 | } |
---|
1025 | |
---|
1026 | /// Получить отображаемое наименование формы |
---|
1027 | /// @return Название формы, если существует, null в ином случае |
---|
1028 | protected function getPrintFormViewName($form_name) { |
---|
1029 | $forms = $this->getPrintFormList(); |
---|
1030 | foreach ($forms as $form) { |
---|
1031 | if ($form['name'] == $form_name) { |
---|
1032 | return $form['desc']; |
---|
1033 | } |
---|
1034 | } |
---|
1035 | return null; |
---|
1036 | } |
---|
1037 | |
---|
1038 | /** |
---|
1039 | * Сформировать печатную форму |
---|
1040 | * @param string $form_name Имя печатной формы |
---|
1041 | * @param bool $to_str Вернуть ли данные в виде строки |
---|
1042 | * @return string Если $to_str == true - возвращает сформированный документ, false в ином случае |
---|
1043 | * @throws AccessException |
---|
1044 | * @throws NotFoundException |
---|
1045 | */ |
---|
1046 | protected function makePrintForm($form_name, $to_str = false) { |
---|
1047 | $aclFlag = \acl::GET_PRINTDRAFT; |
---|
1048 | if ($this->doc_data['ok']) { |
---|
1049 | $aclFlag = \acl::GET_PRINTFORM; |
---|
1050 | } |
---|
1051 | list(,$form_acl) = explode(':', $form_name); |
---|
1052 | \acl::accessGuard("doc.{$this->typename}.$form_acl", $aclFlag); |
---|
1053 | \acl::accessGuard([ 'firm.global', "firm.{$this->doc_data['firm_id']}" ], $aclFlag); |
---|
1054 | return $this->makePrintFormNoACLTest($form_name, $to_str); |
---|
1055 | } |
---|
1056 | |
---|
1057 | /** |
---|
1058 | * Сформировать печатную форму, не проверяя привилегии |
---|
1059 | * @param string $form_name Имя печатной формы |
---|
1060 | * @param bool $to_str Вернуть ли данные в виде строки |
---|
1061 | * @return string Если $to_str == true - возвращает сформированный документ, false в ином случае |
---|
1062 | * @throws NotFoundException |
---|
1063 | */ |
---|
1064 | public function makePrintFormNoACLTest($form_name, $to_str = false) { |
---|
1065 | if (!$this->isPrintFormExists($form_name)) { |
---|
1066 | throw new \NotFoundException('Печатная форма ' . html_out($form_name) . ' не зарегистрирована'); |
---|
1067 | } |
---|
1068 | $f_param = explode(':', $form_name); |
---|
1069 | if ($f_param[0] == 'int') { |
---|
1070 | $method = ''; |
---|
1071 | foreach ($this->PDFForms as $form) { |
---|
1072 | if ($form['name'] == $f_param[1]) { |
---|
1073 | $method = $form['method']; |
---|
1074 | } |
---|
1075 | } |
---|
1076 | return $this->$method($to_str); |
---|
1077 | } elseif ($f_param[0] == 'ext') { |
---|
1078 | $class_name = '\\doc\\printforms\\' . $this->typename . '\\' . $f_param[1]; |
---|
1079 | $print_obj = new $class_name; |
---|
1080 | $print_obj->setDocument($this); |
---|
1081 | $print_obj->initForm(); |
---|
1082 | $print_obj->make(); |
---|
1083 | return $print_obj->outData($to_str); |
---|
1084 | } elseif ($f_param[0] == 'csv') { |
---|
1085 | return $this->CSVExport($to_str); |
---|
1086 | } else { |
---|
1087 | throw new \NotFoundException('Неверный тип печатной формы'); |
---|
1088 | } |
---|
1089 | } |
---|
1090 | |
---|
1091 | /// Отправка документа по факсу |
---|
1092 | /// @param $form_name Имя печатной формы |
---|
1093 | final function sendFax($form_name = '') { |
---|
1094 | global $tmpl, $db; |
---|
1095 | $tmpl->ajax = 1; |
---|
1096 | try { |
---|
1097 | if ($form_name == '') { |
---|
1098 | $agent = new \models\agent($this->doc_data['agent']); |
---|
1099 | $ret_data = array( |
---|
1100 | 'response' => 'item_list', |
---|
1101 | 'faxnum' => $agent->getFaxNum(), |
---|
1102 | 'content' => $this->getPrintFormList() |
---|
1103 | ); |
---|
1104 | $tmpl->setContent(json_encode($ret_data, JSON_UNESCAPED_UNICODE)); |
---|
1105 | } else { |
---|
1106 | $faxnum = request('faxnum'); |
---|
1107 | if ($faxnum == '') { |
---|
1108 | throw new \Exception('Номер факса не указан'); |
---|
1109 | } |
---|
1110 | if (!preg_match('/^\+\d{8,15}$/', $faxnum)) { |
---|
1111 | throw new \Exception("Номер факса $faxnum указан в недопустимом формате"); |
---|
1112 | } |
---|
1113 | include_once('sendfax.php'); |
---|
1114 | $data = $this->makePrintForm($form_name, true); |
---|
1115 | $fs = new FaxSender(); |
---|
1116 | $fs->setFileBuf($data); |
---|
1117 | $fs->setFaxNumber($faxnum); |
---|
1118 | |
---|
1119 | $res = $db->query("SELECT `worker_email` FROM `users_worker_info` WHERE `user_id`='{$_SESSION['uid']}'"); |
---|
1120 | if ($res->num_rows) { |
---|
1121 | list($email) = $res->fetch_row(); |
---|
1122 | $fs->setNotifyMail($email); |
---|
1123 | } |
---|
1124 | $res = $fs->send(); |
---|
1125 | $tmpl->setContent("{'response': 'send'}"); |
---|
1126 | doc_log("Send FAX", $faxnum, 'doc', $this->id); |
---|
1127 | } |
---|
1128 | } catch (Exception $e) { |
---|
1129 | $tmpl->setContent("{response: 'err', text: '" . $e->getMessage() . "'}"); |
---|
1130 | } |
---|
1131 | } |
---|
1132 | |
---|
1133 | /** Отправка документа по факсу на указанный номер |
---|
1134 | * |
---|
1135 | * @param $form_name Имя формы отправляемого документа |
---|
1136 | * @param $faxnum Номер факса получателя |
---|
1137 | */ |
---|
1138 | final function sendFaxTo($form_name, $faxnum) { |
---|
1139 | global $db; |
---|
1140 | if ($faxnum == '') { |
---|
1141 | throw new \Exception('Номер факса не указан'); |
---|
1142 | } |
---|
1143 | if (!preg_match('/^\+\d{8,15}$/', $faxnum)) { |
---|
1144 | throw new \Exception("Номер факса $faxnum указан в недопустимом формате"); |
---|
1145 | } |
---|
1146 | include_once('sendfax.php'); |
---|
1147 | $data = $this->makePrintFormNoACLTest($form_name, true); |
---|
1148 | $fs = new \FaxSender(); |
---|
1149 | $fs->setFileBuf($data); |
---|
1150 | $fs->setFaxNumber($faxnum); |
---|
1151 | |
---|
1152 | $res = $db->query("SELECT `worker_email` FROM `users_worker_info` WHERE `user_id`='{$_SESSION['uid']}'"); |
---|
1153 | if ($res->num_rows) { |
---|
1154 | list($email) = $res->fetch_row(); |
---|
1155 | $fs->setNotifyMail($email); |
---|
1156 | } |
---|
1157 | $res = $fs->send(); |
---|
1158 | doc_log("Send FAX", $faxnum, 'doc', $this->id); |
---|
1159 | return true; |
---|
1160 | } |
---|
1161 | |
---|
1162 | function getExtensionFromMIME($mime) { |
---|
1163 | switch ($mime) { |
---|
1164 | case 'text/csv': |
---|
1165 | return '.csv'; |
---|
1166 | case 'application/vnd.ms-excel': |
---|
1167 | return '.xls'; |
---|
1168 | case 'application/vnd.oasis.opendocument.spreadsheet': |
---|
1169 | return '.ods'; |
---|
1170 | case 'application/pdf': |
---|
1171 | default: |
---|
1172 | return '.pdf'; |
---|
1173 | } |
---|
1174 | } |
---|
1175 | |
---|
1176 | /// Отправка документа по электронной почте |
---|
1177 | /// @param $form_name Имя печатной формы |
---|
1178 | final function sendEMail($form_name = '') { |
---|
1179 | global $tmpl, $db; |
---|
1180 | $tmpl->ajax = 1; |
---|
1181 | try { |
---|
1182 | if ($form_name == '') { |
---|
1183 | $agent = new \models\agent($this->doc_data['agent']); |
---|
1184 | $ret_data = array( |
---|
1185 | 'response' => 'item_list', |
---|
1186 | 'email' => $agent->getEmail(), |
---|
1187 | 'content' => $this->getCSVPrintFormList() |
---|
1188 | ); |
---|
1189 | $tmpl->setContent(json_encode($ret_data, JSON_UNESCAPED_UNICODE)); |
---|
1190 | } else { |
---|
1191 | $email = request('email'); |
---|
1192 | $comment = request('comment'); |
---|
1193 | if ($email == '') { |
---|
1194 | throw new \Exception('Адрес электронной почты не указан!'); |
---|
1195 | } else { |
---|
1196 | $data = $this->makePrintForm($form_name, true); |
---|
1197 | $mime = $this->getPrintFormMime($form_name); |
---|
1198 | $extension = $this->getExtensionFromMIME($mime); |
---|
1199 | |
---|
1200 | $fname = $this->typename . '_' . str_replace(":", "_", $form_name) . $extension; |
---|
1201 | $viewname = $this->getPrintFormViewName($form_name) . ' (' . $this->viewname . ')'; |
---|
1202 | $this->sendDocByEMail($email, $comment, $viewname, $data, $fname); |
---|
1203 | $tmpl->setContent("{'response': 'send'}"); |
---|
1204 | doc_log("Send email", $email, 'doc', $this->id); |
---|
1205 | } |
---|
1206 | } |
---|
1207 | } catch (Exception $e) { |
---|
1208 | $tmpl->setContent("{'response':'err','text':'" . $e->getMessage() . "'}"); |
---|
1209 | } |
---|
1210 | } |
---|
1211 | |
---|
1212 | /** Отправка документа по электронной почте |
---|
1213 | * |
---|
1214 | * @param $form_name Имя печатной формы |
---|
1215 | * @param $email Адрес электронной почты |
---|
1216 | * @param string $text Текст сообщения электронной почты |
---|
1217 | */ |
---|
1218 | final function sendEmailTo($form_name, $email, $text='') { |
---|
1219 | if ($email == '') { |
---|
1220 | throw new \Exception('Адрес электронной почты не указан!'); |
---|
1221 | } |
---|
1222 | $data = $this->makePrintFormNoACLTest($form_name, true); |
---|
1223 | $mime = $this->getPrintFormMime($form_name); |
---|
1224 | $extension = $this->getExtensionFromMIME($mime); |
---|
1225 | |
---|
1226 | $fname = $this->typename . '_' . str_replace(":", "_", $form_name) . $extension; |
---|
1227 | $viewname = $this->getPrintFormViewName($form_name) . ' (' . $this->viewname . ')'; |
---|
1228 | $this->sendDocByEMail($email, $text, $viewname, $data, $fname); |
---|
1229 | doc_log("Send email", $email, 'doc', $this->id); |
---|
1230 | return true; |
---|
1231 | } |
---|
1232 | |
---|
1233 | /// Печать документа |
---|
1234 | /// @param $form_name Имя печатной формы |
---|
1235 | /// @param $user_print Если истина - документ запрошен из пользовательского раздела |
---|
1236 | function printForm($form_name = '') { |
---|
1237 | global $tmpl; |
---|
1238 | $tmpl->ajax = 1; |
---|
1239 | if ($form_name == '') { |
---|
1240 | $ret_data = array( |
---|
1241 | 'response' => 'item_list', |
---|
1242 | 'content' => $this->getCSVPrintFormList() |
---|
1243 | ); |
---|
1244 | $tmpl->setContent(json_encode($ret_data, JSON_UNESCAPED_UNICODE)); |
---|
1245 | } else { |
---|
1246 | $this->makePrintForm($form_name); |
---|
1247 | $this->sentZEvent('print'); |
---|
1248 | doc_log("PRINT", $form_name, 'doc', $this->id); |
---|
1249 | } |
---|
1250 | } |
---|
1251 | |
---|
1252 | /// Печать документа посетителем сайта / не сотрудником |
---|
1253 | /// @param $form_name Имя печатной формы |
---|
1254 | /// @param $user_print Если истина - документ запрошен из пользовательского раздела |
---|
1255 | function printFormFromCabinet($form_name) { |
---|
1256 | global $tmpl; |
---|
1257 | $tmpl->ajax = 1; |
---|
1258 | if ($form_name == '') { |
---|
1259 | throw new \NotFoundException('Печатная форма не выбрана'); |
---|
1260 | } else { |
---|
1261 | $this->makePrintFormNoACLTest($form_name); |
---|
1262 | $this->sentZEvent('userprint'); |
---|
1263 | doc_log("USERPRINT", $form_name, 'doc', $this->id); |
---|
1264 | } |
---|
1265 | } |
---|
1266 | |
---|
1267 | /// Выполнить удаление документа. Если есть зависимости - удаление не производится. |
---|
1268 | function delExec() { |
---|
1269 | global $db; |
---|
1270 | if ($this->doc_data['ok']) { |
---|
1271 | throw new \Exception("Нельзя удалить проведённый документ"); |
---|
1272 | } |
---|
1273 | $res = $db->query("SELECT `id`, `mark_del` FROM `doc_list` WHERE `p_doc`='{$this->id}'"); |
---|
1274 | if ($res->num_rows) { |
---|
1275 | throw new \Exception("Нельзя удалить документ с неудалёнными потомками"); |
---|
1276 | } |
---|
1277 | $db->query("DELETE FROM `doc_list_pos` WHERE `doc`='{$this->id}'"); |
---|
1278 | $db->query("DELETE FROM `doc_dopdata` WHERE `doc`='{$this->id}'"); |
---|
1279 | $db->query("DELETE FROM `doc_list` WHERE `id`='{$this->id}'"); |
---|
1280 | } |
---|
1281 | |
---|
1282 | /// Сделать документ потомком указанного документа и вернуть резутьтат в json формате |
---|
1283 | function connectJson($p_doc) { |
---|
1284 | try { |
---|
1285 | \acl::accessGuard('doc.' . $this->typename, \acl::UPDATE); |
---|
1286 | if ($this->doc_data['firm_id'] > 0) { |
---|
1287 | \acl::accessGuard([ 'firm.global', 'firm.' . $this->doc_data['firm_id']], \acl::UPDATE); |
---|
1288 | } |
---|
1289 | $this->subordinate($p_doc); |
---|
1290 | return " { \"response\": \"connect_ok\" }"; |
---|
1291 | } catch (Exception $e) { |
---|
1292 | return " { \"response\": \"error\", \"message\": \"" . $e->getMessage() . "\" }"; |
---|
1293 | } |
---|
1294 | } |
---|
1295 | |
---|
1296 | /// отправка документа по электронной почте |
---|
1297 | function sendDocByEMail($email, $comment, $docname, $data, $filename, $body = '') { |
---|
1298 | global $CONFIG, $db; |
---|
1299 | $pref = \pref::getInstance(); |
---|
1300 | $res_autor = $db->query("SELECT `worker_real_name`, `worker_phone`, `worker_email` FROM `users_worker_info` |
---|
1301 | WHERE `user_id`='" . $this->doc_data['user'] . "'"); |
---|
1302 | $doc_autor = $res_autor->fetch_assoc(); |
---|
1303 | $agent = new \models\agent($this->doc_data['agent']); |
---|
1304 | |
---|
1305 | $email_message = new \email_message(); |
---|
1306 | $email_message->default_charset = "UTF-8"; |
---|
1307 | if ($agent->fullname) { |
---|
1308 | $email_message->SetEncodedEmailHeader("To", $email, $agent->fullname); |
---|
1309 | } else if ($agent->name) { |
---|
1310 | $email_message->SetEncodedEmailHeader("To", $email, $agent->name); |
---|
1311 | } else { |
---|
1312 | $email_message->SetEncodedEmailHeader("To", $email, $email); |
---|
1313 | } |
---|
1314 | |
---|
1315 | $email_message->SetEncodedHeader("Subject", "{$pref->site_display_name} - $docname ({$pref->site_name})"); |
---|
1316 | |
---|
1317 | if (!@$doc_autor['worker_email']) { |
---|
1318 | $email_message->SetEncodedEmailHeader("From", $pref->site_email, "Почтовый робот {$pref->site_name}"); |
---|
1319 | $email_message->SetHeader("Sender", $pref->site_email); |
---|
1320 | $text_message = "Здравствуйте, {$agent->fullname}!\n" |
---|
1321 | . "Во вложении находится заказанный Вами документ ($docname) от {$pref->site_display_name} ({$pref->site_name})\n\n" |
---|
1322 | . "$comment\n\n" |
---|
1323 | . "Сообщение сгенерировано автоматически, отвечать на него не нужно!\n" |
---|
1324 | . "Для переписки используйте адрес, указанный в контактной информации на сайте http://{$pref->site_name}!"; |
---|
1325 | } else { |
---|
1326 | $email_message->SetEncodedEmailHeader("From", $doc_autor['worker_email'], $doc_autor['worker_real_name']); |
---|
1327 | $email_message->SetHeader("Sender", $doc_autor['worker_email']); |
---|
1328 | $text_message = "Здравствуйте, {$agent->fullname}!\n" |
---|
1329 | . "Во вложении находится заказанный Вами документ ($docname) от {$pref->site_name}\n\n$comment\n\n" |
---|
1330 | . "Ответственный сотрудник: {$doc_autor['worker_real_name']}\n" |
---|
1331 | . "Контактный телефон: {$doc_autor['worker_phone']}\n" |
---|
1332 | . "Электронная почта (e-mail): {$doc_autor['worker_email']}\n" |
---|
1333 | . "Отправитель: {$_SESSION['name']}"; |
---|
1334 | } |
---|
1335 | if ($body) { |
---|
1336 | $email_message->AddQuotedPrintableTextPart($body); |
---|
1337 | } else { |
---|
1338 | $email_message->AddQuotedPrintableTextPart($text_message); |
---|
1339 | } |
---|
1340 | |
---|
1341 | $text_attachment = array( |
---|
1342 | "Data" => $data, |
---|
1343 | "Name" => $filename, |
---|
1344 | "Content-Type" => "automatic/name", |
---|
1345 | "Disposition" => "attachment" |
---|
1346 | ); |
---|
1347 | $email_message->AddFilePart($text_attachment); |
---|
1348 | |
---|
1349 | $error = $email_message->Send(); |
---|
1350 | |
---|
1351 | if (strcmp($error, "")) { |
---|
1352 | throw new \Exception($error); |
---|
1353 | } else { |
---|
1354 | return 0; |
---|
1355 | } |
---|
1356 | } |
---|
1357 | |
---|
1358 | /// Обработка отправки запроса на отмену документа |
---|
1359 | protected function sendPetition() { |
---|
1360 | global $db; |
---|
1361 | $ret = array('object' => 'send_petition', 'response' => 'success'); |
---|
1362 | try { |
---|
1363 | $text = request('text'); |
---|
1364 | $pref = pref::getInstance(); |
---|
1365 | if (mb_strlen($text) < 8) { |
---|
1366 | throw new Exception('Сообщение слишком короткое! Опишите причину подробнее!'); |
---|
1367 | } |
---|
1368 | $res = $db->query("SELECT `users`.`reg_email`, `users_worker_info`.`worker_email` FROM `users` |
---|
1369 | LEFT JOIN `users_worker_info` ON `users_worker_info`.`user_id`=`users`.`id` |
---|
1370 | WHERE `id`='{$_SESSION['uid']}'"); |
---|
1371 | $user_info = $res->fetch_array(); |
---|
1372 | if ($user_info['worker_email'] != '') { |
---|
1373 | $from = $user_info['worker_email']; |
---|
1374 | } else if ($user_info['reg_email'] != '') { |
---|
1375 | $from = $user_info['reg_email']; |
---|
1376 | } else { |
---|
1377 | $from = \cfg::get('site', 'doc_adm_email'); |
---|
1378 | } |
---|
1379 | |
---|
1380 | $proto = @$_SERVER['HTTPS'] ? 'https' : 'http'; |
---|
1381 | $ip = getenv("REMOTE_ADDR"); |
---|
1382 | $date = date("Y-m-d H:i:s", $this->doc_data['date']); |
---|
1383 | $txt = "Здравствуйте!\nПользователь {$_SESSION['name']} просит Вас отменить проводку документа *{$this->viewname}* с ID: {$this->id}," |
---|
1384 | . " {$this->doc_data['altnum']}{$this->doc_data['subtype']} от {$date} на сумму {$this->doc_data['sum']}." |
---|
1385 | . " Клиент {$this->doc_data['agent_name']}.\n{$proto}://{$_SERVER["HTTP_HOST"]}/doc.php?mode=body&doc={$this->id} \n" |
---|
1386 | . "Цель отмены: $text.\n" |
---|
1387 | . "IP: $ip\n" |
---|
1388 | . "Пожалуйста, дайте ответ на это письмо на $from, как в случае отмены документа, так и об отказе отмены!"; |
---|
1389 | |
---|
1390 | if (\cfg::get('site', 'doc_adm_email')) { |
---|
1391 | mailto(\cfg::get('site', 'doc_adm_email'), 'Запрос на отмену проведения документа', $txt, $from); |
---|
1392 | } |
---|
1393 | |
---|
1394 | if (\cfg::get('site', 'doc_adm_jid') && \cfg::get('xmpp', 'host')) { |
---|
1395 | require_once(\cfg::getroot('location') . '/common/XMPPHP/XMPP.php'); |
---|
1396 | $xmppclient = new \XMPPHP\XMPP(\cfg::get('xmpp', 'host'), \cfg::get('xmpp', 'port'), \cfg::get('xmpp', 'login'), \cfg::get('xmpp', 'pass') |
---|
1397 | , 'MultiMag r' . MULTIMAG_REV); |
---|
1398 | $xmppclient->connect(); |
---|
1399 | $xmppclient->processUntil('session_start'); |
---|
1400 | $xmppclient->presence(); |
---|
1401 | $xmppclient->message(\cfg::get('site', 'doc_adm_jid'), $txt); |
---|
1402 | $xmppclient->disconnect(); |
---|
1403 | } |
---|
1404 | $ret['message'] = "Сообщение было отправлено уполномоченному лицу! Ответ о снятии проводки придёт вам на e-mail!"; |
---|
1405 | } catch (\XMPPHP\Exception $e) { |
---|
1406 | writeLogException($e); |
---|
1407 | $ret = array('object' => 'send_petition', 'response' => 'error', |
---|
1408 | 'errormessage' => "Невозможно отправить сообщение по XMPP: " . $e->getMessage() |
---|
1409 | ); |
---|
1410 | } catch (\Exception $e) { |
---|
1411 | $ret = array('object' => 'send_petition', 'response' => 'error', 'errormessage' => $e->getMessage()); |
---|
1412 | } |
---|
1413 | return json_encode($ret, JSON_UNESCAPED_UNICODE); |
---|
1414 | } |
---|
1415 | |
---|
1416 | function service() { |
---|
1417 | global $tmpl; |
---|
1418 | $tmpl->ajax = 1; |
---|
1419 | $opt = request('opt'); |
---|
1420 | $pos = rcvint('pos'); |
---|
1421 | $this->_service($opt, $pos); |
---|
1422 | } |
---|
1423 | |
---|
1424 | /// Служебные опции |
---|
1425 | function _service($opt, $pos) { |
---|
1426 | global $tmpl; |
---|
1427 | $tmpl->ajax = 1; |
---|
1428 | |
---|
1429 | if ($this->sklad_editor_enable) { |
---|
1430 | include_once('doc.poseditor.php'); |
---|
1431 | $poseditor = new DocPosEditor($this); |
---|
1432 | $poseditor->setAllowNegativeCounts($this->allow_neg_cnt); |
---|
1433 | } |
---|
1434 | |
---|
1435 | $peopt = request('peopt'); // Опции редактора списка товаров |
---|
1436 | |
---|
1437 | \acl::accessGuard('doc.' . $this->typename, \acl::VIEW); |
---|
1438 | if ($this->doc_data['firm_id'] > 0) { |
---|
1439 | \acl::accessGuard([ 'firm.global', 'firm.' . $this->doc_data['firm_id']], \acl::VIEW); |
---|
1440 | } |
---|
1441 | |
---|
1442 | switch ($opt) { |
---|
1443 | case 'link_info': |
---|
1444 | $ret = $this->getLinkInfo(); |
---|
1445 | $tmpl->setContent(json_encode($ret, JSON_UNESCAPED_UNICODE)); |
---|
1446 | return 1; |
---|
1447 | case 'petition': |
---|
1448 | $tmpl->setContent($this->sendPetition()); |
---|
1449 | return 1; |
---|
1450 | case 'getheader': |
---|
1451 | $ret = array( |
---|
1452 | 'response' => 'success', |
---|
1453 | 'object' => 'getheader', |
---|
1454 | 'content' => $this->getDocumentHeader(), |
---|
1455 | ); |
---|
1456 | $tmpl->setContent(json_encode($ret, JSON_UNESCAPED_UNICODE)); |
---|
1457 | return 1; |
---|
1458 | } |
---|
1459 | |
---|
1460 | /// Операции, для которых нужен доступ только на чтение |
---|
1461 | switch ($peopt) { |
---|
1462 | case 'jget': // Json-вариант списка товаров |
---|
1463 | // TODO: пересчет цены перенести внутрь poseditor |
---|
1464 | $this->recalcSum(); |
---|
1465 | $doc_content = $poseditor->GetAllContent(); |
---|
1466 | $tmpl->addContent($doc_content); |
---|
1467 | return 1; |
---|
1468 | case 'jgetgroups': |
---|
1469 | $doc_content = $poseditor->getGroupList(); |
---|
1470 | $tmpl->addContent($doc_content); |
---|
1471 | return 1; |
---|
1472 | case 'jgpi': // Получение данных наименования |
---|
1473 | $pos = rcvint('pos'); |
---|
1474 | $tmpl->addContent($poseditor->GetPosInfo($pos)); |
---|
1475 | return 1; |
---|
1476 | case 'jsklad': // Получение номенклатуры выбранной группы |
---|
1477 | $group_id = rcvint('group_id'); |
---|
1478 | $str = "{ response: 'sklad_list', group: '$group_id', content: [" . $poseditor->GetSkladList($group_id) . "] }"; |
---|
1479 | $tmpl->setContent($str); |
---|
1480 | return 1; |
---|
1481 | case 'jsklads': // Поиск по подстроке по складу |
---|
1482 | $s = request('s'); |
---|
1483 | $str = "{ response: 'sklad_list', content: " . $poseditor->SearchSkladList($s) . " }"; |
---|
1484 | $tmpl->setContent($str); |
---|
1485 | return 1; |
---|
1486 | } |
---|
1487 | |
---|
1488 | /// TODO: Это тоже переделать! |
---|
1489 | if ($this->doc_data['ok']) { |
---|
1490 | throw new \Exception("Операция не допускается для проведённого документа!"); |
---|
1491 | } |
---|
1492 | switch ($opt) { |
---|
1493 | case 'jdeldoc': // Пометка на удаление |
---|
1494 | $tmpl->setContent($this->serviceDelDoc()); |
---|
1495 | return 1; |
---|
1496 | case 'jundeldoc': // Снять пометку на удаление |
---|
1497 | $tmpl->setContent($this->serviceUnDelDoc()); |
---|
1498 | return 1; |
---|
1499 | case 'merge': // Загрузка номенклатурной таблицы |
---|
1500 | $ret = $this->mergeDocList($poseditor); |
---|
1501 | $tmpl->setContent(json_encode($ret, JSON_UNESCAPED_UNICODE)); |
---|
1502 | return 1; |
---|
1503 | } |
---|
1504 | if ($this->doc_data['mark_del']) { |
---|
1505 | throw new \Exception("Операция не допускается для документа, отмеченного для удаления!"); |
---|
1506 | } |
---|
1507 | |
---|
1508 | /// Операции, изменяющие документ |
---|
1509 | switch ($peopt) { |
---|
1510 | case 'jadd': // Json вариант добавления позиции |
---|
1511 | \acl::accessGuard('doc.' . $this->typename, \acl::UPDATE); |
---|
1512 | if ($this->doc_data['firm_id'] > 0) { |
---|
1513 | \acl::accessGuard([ 'firm.global', 'firm.' . $this->doc_data['firm_id']], \acl::UPDATE); |
---|
1514 | } |
---|
1515 | $pe_pos = rcvint('pe_pos'); |
---|
1516 | $tmpl->setContent($poseditor->AddPos($pe_pos)); |
---|
1517 | break; |
---|
1518 | case 'jdel': // Json вариант удаления строки |
---|
1519 | \acl::accessGuard('doc.' . $this->typename, \acl::UPDATE); |
---|
1520 | if ($this->doc_data['firm_id'] > 0) { |
---|
1521 | \acl::accessGuard([ 'firm.global', 'firm.' . $this->doc_data['firm_id']], \acl::UPDATE); |
---|
1522 | } |
---|
1523 | $line_id = rcvint('line_id'); |
---|
1524 | $tmpl->setContent($poseditor->Removeline($line_id)); |
---|
1525 | break; |
---|
1526 | case 'jup': // Json вариант обновления |
---|
1527 | \acl::accessGuard('doc.' . $this->typename, \acl::UPDATE); |
---|
1528 | if ($this->doc_data['firm_id'] > 0) { |
---|
1529 | \acl::accessGuard([ 'firm.global', 'firm.' . $this->doc_data['firm_id']], \acl::UPDATE); |
---|
1530 | } |
---|
1531 | $line_id = rcvint('line_id'); |
---|
1532 | $value = request('value'); |
---|
1533 | $type = request('type'); |
---|
1534 | // TODO: пересчет цены перенести внутрь poseditor |
---|
1535 | $tmpl->setContent($poseditor->UpdateLine($line_id, $type, $value)); |
---|
1536 | break; |
---|
1537 | case 'jsn': // Серийные номера |
---|
1538 | \acl::accessGuard('doc.' . $this->typename, \acl::UPDATE); |
---|
1539 | if ($this->doc_data['firm_id'] > 0) { |
---|
1540 | \acl::accessGuard([ 'firm.global', 'firm.' . $this->doc_data['firm_id']], \acl::UPDATE); |
---|
1541 | } |
---|
1542 | $action = request('a'); |
---|
1543 | $line_id = request('line'); |
---|
1544 | $data = request('data'); |
---|
1545 | $tmpl->setContent($poseditor->SerialNum($action, $line_id, $data)); |
---|
1546 | break; |
---|
1547 | case 'jrc': // Сброс цен |
---|
1548 | \acl::accessGuard('doc.' . $this->typename, \acl::UPDATE); |
---|
1549 | if ($this->doc_data['firm_id'] > 0) { |
---|
1550 | \acl::accessGuard([ 'firm.global', 'firm.' . $this->doc_data['firm_id']], \acl::UPDATE); |
---|
1551 | } |
---|
1552 | $poseditor->resetPrices(); |
---|
1553 | break; |
---|
1554 | case 'jorder': // Сортировка наименований |
---|
1555 | \acl::accessGuard('doc.' . $this->typename, \acl::UPDATE); |
---|
1556 | if ($this->doc_data['firm_id'] > 0) { |
---|
1557 | \acl::accessGuard([ 'firm.global', 'firm.' . $this->doc_data['firm_id']], \acl::UPDATE); |
---|
1558 | } |
---|
1559 | $by = request('by'); |
---|
1560 | $poseditor->reOrder($by); |
---|
1561 | break; |
---|
1562 | default: |
---|
1563 | return 0; |
---|
1564 | } |
---|
1565 | return 1; |
---|
1566 | } |
---|
1567 | |
---|
1568 | /// Получить многомерный массив с данными заголовка документа |
---|
1569 | public function getDocumentHeader() { |
---|
1570 | global $db, $CONFIG; |
---|
1571 | $ret = array(); |
---|
1572 | |
---|
1573 | if ($this->doc_type == 0) { |
---|
1574 | throw new Exception("Невозможно получить заголовок документа неизвестного типа"); |
---|
1575 | } else { |
---|
1576 | // Динамические: баланс, бонусы, список договоров агента |
---|
1577 | $ret['id'] = $this->id; |
---|
1578 | $ret['viewname'] = $this->viewname; |
---|
1579 | $ret['type'] = $this->doc_type; |
---|
1580 | $ret['typename'] = $this->typename; |
---|
1581 | $ret['altnum'] = $this->doc_data['altnum']; |
---|
1582 | $ret['subtype'] = $this->doc_data['subtype']; |
---|
1583 | $ret['mark_del'] = $this->doc_data['mark_del']; |
---|
1584 | $ret['firm_id'] = $this->doc_data['firm_id']; |
---|
1585 | $ret['comment'] = $this->doc_data['comment']; |
---|
1586 | $ret['created'] = $this->doc_data['created']; |
---|
1587 | $ret['ok'] = $this->doc_data['ok']; |
---|
1588 | $ret['p_doc'] = $this->doc_data['p_doc']; |
---|
1589 | |
---|
1590 | $fields = explode(' ', $this->header_fields); |
---|
1591 | $ret['header_fields'] = $fields; |
---|
1592 | |
---|
1593 | foreach ($fields as $f) { |
---|
1594 | switch ($f) { |
---|
1595 | case 'agent': |
---|
1596 | $ret['agent_id'] = $this->doc_data['agent']; |
---|
1597 | $ret['contract_id'] = $this->doc_data['contract']; |
---|
1598 | break; |
---|
1599 | case 'sklad': |
---|
1600 | $ret['store_id'] = $this->doc_data['sklad']; |
---|
1601 | break; |
---|
1602 | case 'kassa': |
---|
1603 | $ret['cash_id'] = $this->doc_data['kassa']; |
---|
1604 | break; |
---|
1605 | case 'bank': |
---|
1606 | $ret['bank_id'] = $this->doc_data['bank']; |
---|
1607 | break; |
---|
1608 | case 'cena': |
---|
1609 | $ret['price_id'] = $this->dop_data['cena']; |
---|
1610 | break; |
---|
1611 | case 'sum': |
---|
1612 | $ret['sum'] = $this->doc_data['sum']; |
---|
1613 | break; |
---|
1614 | } |
---|
1615 | } |
---|
1616 | |
---|
1617 | if (isset($CONFIG['site']['default_firm'])) { |
---|
1618 | $ret['default_firm_id'] = $CONFIG['site']['default_firm']; |
---|
1619 | } |
---|
1620 | $ret['dop_buttons'] = $this->getDopButtons(); |
---|
1621 | $firm_ldo = new \Models\LDO\firmnames(); |
---|
1622 | $ret['firm_names'] = $firm_ldo->getData(); |
---|
1623 | |
---|
1624 | |
---|
1625 | if ($this->doc_data['date']) { |
---|
1626 | $ret['date'] = date("Y-m-d H:i:s", $this->doc_data['date']); |
---|
1627 | } else { |
---|
1628 | $ret['date'] = date("Y-m-d H:i:s"); |
---|
1629 | } |
---|
1630 | |
---|
1631 | if (in_array('agent', $ret['header_fields'])) { |
---|
1632 | $contract_list = array(); |
---|
1633 | $res = $db->query("SELECT `doc_list`.`id`, `doc_list`.`altnum`, `doc_list`.`subtype`, `doc_dopdata`.`value` AS `name`, `doc_list`.`date` |
---|
1634 | FROM `doc_list` |
---|
1635 | LEFT JOIN `doc_dopdata` ON `doc_dopdata`.`doc`=`doc_list`.`id` AND `doc_dopdata`.`param`='name' |
---|
1636 | WHERE `agent`='{$this->doc_data['agent']}' AND `type`='14' AND `firm_id`='{$this->doc_data['firm_id']}'"); |
---|
1637 | while ($line = $res->fetch_assoc()) { |
---|
1638 | $line['date'] = date("Y-m-d H:i:s", $line['date']); |
---|
1639 | $contract_list[] = $line; |
---|
1640 | } |
---|
1641 | $ret['agent_info'] = array( |
---|
1642 | 'name' => $this->doc_data['agent_name'], |
---|
1643 | 'balance' => agentCalcDebt($this->doc_data['agent']), |
---|
1644 | 'bonus' => docCalcBonus($this->doc_data['agent']), |
---|
1645 | 'contract_list' => $contract_list, |
---|
1646 | 'dishonest' => $this->doc_data['agent_dishonest'], |
---|
1647 | ); |
---|
1648 | } |
---|
1649 | $ret['ext_fields'] = $this->getExtControls(); |
---|
1650 | $ret = array_merge($this->dop_data, $this->text_data, $ret); |
---|
1651 | } |
---|
1652 | return $ret; |
---|
1653 | } |
---|
1654 | |
---|
1655 | /// обновить заголовок документа данными из массива |
---|
1656 | public function updateDocumentHeader($data) { |
---|
1657 | $doc_data = array(); |
---|
1658 | $dop_data = array(); |
---|
1659 | if(isset($data['altnum'])) { |
---|
1660 | $doc_data['altnum'] = $data['altnum']; |
---|
1661 | } |
---|
1662 | if(isset($data['subtype'])) { |
---|
1663 | $doc_data['subtype'] = $data['subtype']; |
---|
1664 | } |
---|
1665 | if(isset($data['firm_id'])) { |
---|
1666 | $doc_data['firm_id'] = $data['firm_id']; |
---|
1667 | } |
---|
1668 | if(isset($data['comment'])) { |
---|
1669 | $doc_data['comment'] = $data['comment']; |
---|
1670 | } |
---|
1671 | |
---|
1672 | $fields = explode(' ', $this->header_fields); |
---|
1673 | foreach ($fields as $f) { |
---|
1674 | switch ($f) { |
---|
1675 | case 'agent': |
---|
1676 | if (isset($data['agent_id'])) { |
---|
1677 | $doc_data['agent'] = $data['agent_id']; |
---|
1678 | } |
---|
1679 | if (isset($data['contract_id'])) { |
---|
1680 | $doc_data['contract'] = $data['contract_id']; |
---|
1681 | } |
---|
1682 | break; |
---|
1683 | case 'sklad': |
---|
1684 | if (isset($data['store_id'])) { |
---|
1685 | $doc_data['sklad'] = $data['store_id']; |
---|
1686 | } |
---|
1687 | break; |
---|
1688 | case 'kassa': |
---|
1689 | if (isset($data['cash_id'])) { |
---|
1690 | $doc_data['kassa'] = $data['cash_id']; |
---|
1691 | } |
---|
1692 | break; |
---|
1693 | case 'bank': |
---|
1694 | if (isset($data['bank_id'])) { |
---|
1695 | $doc_data['bank'] = $data['bank_id']; |
---|
1696 | } |
---|
1697 | break; |
---|
1698 | case 'cena': |
---|
1699 | if (isset($data['price_id'])) { |
---|
1700 | $dop_data['cena'] = $data['price_id']; |
---|
1701 | } |
---|
1702 | break; |
---|
1703 | case 'sum': |
---|
1704 | if (isset($data['sum'])) { |
---|
1705 | $doc_data['sum'] = $data['sum']; |
---|
1706 | } |
---|
1707 | break; |
---|
1708 | } |
---|
1709 | } |
---|
1710 | foreach($this->def_dop_data as $name => $value) { |
---|
1711 | if (isset($data[$name])) { |
---|
1712 | $dop_data[$name] = $data[$name]; |
---|
1713 | } |
---|
1714 | } |
---|
1715 | $extcontrols = $this->getExtControls(); |
---|
1716 | foreach ($extcontrols as $ex_name => $ex_data) { |
---|
1717 | switch($ex_data['type']) { |
---|
1718 | case 'text': |
---|
1719 | case 'select': |
---|
1720 | case 'status': |
---|
1721 | if (isset($data[$ex_name])) { |
---|
1722 | $dop_data[$ex_name] = $data[$ex_name]; |
---|
1723 | } |
---|
1724 | break; |
---|
1725 | case 'checkbox': |
---|
1726 | if (isset($data[$ex_name])) { |
---|
1727 | $dop_data[$ex_name] = $data[$ex_name]?1:0; |
---|
1728 | } |
---|
1729 | break; |
---|
1730 | } |
---|
1731 | } |
---|
1732 | if(count($doc_data)>0) { |
---|
1733 | $this->setDocDataA($doc_data); |
---|
1734 | } |
---|
1735 | if(count($dop_data)>0) { |
---|
1736 | //throw new Exception(json_encode($dop_data)); |
---|
1737 | $this->setDopDataA($dop_data); |
---|
1738 | } |
---|
1739 | } |
---|
1740 | |
---|
1741 | /// Слияние табличной части двух документов |
---|
1742 | protected function mergeDocList($poseditor) { |
---|
1743 | global $db; |
---|
1744 | $from_doc = rcvint('from_doc'); |
---|
1745 | $clear = rcvint('clear'); |
---|
1746 | $no_sum = rcvint('no_sum'); |
---|
1747 | |
---|
1748 | try { |
---|
1749 | if ($from_doc == 0) { |
---|
1750 | throw new Exception("Документ не задан"); |
---|
1751 | } |
---|
1752 | $db->startTransaction(); |
---|
1753 | |
---|
1754 | $res = $db->query("SELECT `id` FROM `doc_list` WHERE `id`=$from_doc"); |
---|
1755 | if (!$res->num_rows) { |
---|
1756 | throw new Exception("Документ не найден"); |
---|
1757 | } |
---|
1758 | |
---|
1759 | if ($clear) { |
---|
1760 | $db->query("DELETE FROM `doc_list_pos` WHERE `doc`='{$this->id}'"); |
---|
1761 | } |
---|
1762 | |
---|
1763 | $res = $db->query("SELECT `doc`, `tovar`, SUM(`cnt`) AS `cnt`, `gtd`, `comm`, `cost`, `page` FROM `doc_list_pos`" |
---|
1764 | . "WHERE `doc`=$from_doc AND `page`=0 GROUP BY `tovar`"); |
---|
1765 | while ($line = $res->fetch_assoc()) { |
---|
1766 | if (!$no_sum) { |
---|
1767 | $poseditor->simpleIncrementPos($line['tovar'], $line['cost'], $line['cnt'], $line['comm']); |
---|
1768 | } else { |
---|
1769 | $poseditor->simpleRewritePos($line['tovar'], $line['cost'], $line['cnt'], $line['comm']); |
---|
1770 | } |
---|
1771 | } |
---|
1772 | doc_log("REWRITE", "", 'doc', $this->id); |
---|
1773 | $db->commit(); |
---|
1774 | $ret = array('response' => 'merge_ok'); |
---|
1775 | } catch (Exception $e) { |
---|
1776 | $ret = array('response' => 'err', 'text' => $e->getMessage()); |
---|
1777 | } |
---|
1778 | return $ret; |
---|
1779 | } |
---|
1780 | |
---|
1781 | /// Получить информацию о связях документа |
---|
1782 | protected function getLinkInfo() { |
---|
1783 | global $db; |
---|
1784 | $childs = array(); |
---|
1785 | $parent = null; |
---|
1786 | if ($this->doc_data['p_doc']) { |
---|
1787 | $res = $db->query("SELECT `doc_list`.`id`, `doc_types`.`name`, `doc_list`.`altnum`, `doc_list`.`subtype`, `doc_list`.`date`, |
---|
1788 | `doc_list`.`ok`, `doc_list`.`sum` FROM `doc_list` |
---|
1789 | LEFT JOIN `doc_types` ON `doc_types`.`id`=`doc_list`.`type` |
---|
1790 | WHERE `doc_list`.`id`='{$this->doc_data['p_doc']}'"); |
---|
1791 | $parent = $res->fetch_assoc(); |
---|
1792 | $parent['vdate'] = date("d.m.Y", $parent['date']); |
---|
1793 | } |
---|
1794 | $res = $db->query("SELECT `doc_list`.`id`, `doc_types`.`name`, `doc_list`.`altnum`, `doc_list`.`subtype`, `doc_list`.`date`, |
---|
1795 | `doc_list`.`ok`, `doc_list`.`sum` FROM `doc_list` |
---|
1796 | LEFT JOIN `doc_types` ON `doc_types`.`id`=`doc_list`.`type` |
---|
1797 | WHERE `doc_list`.`p_doc`='{$this->id}'"); |
---|
1798 | |
---|
1799 | while ($line = $res->fetch_assoc()) { |
---|
1800 | $line['vdate'] = date("d.m.Y", $line['date']); |
---|
1801 | $childs[] = $line; |
---|
1802 | } |
---|
1803 | $ret = array('response' => 'link_info', 'parent' => $parent, 'childs' => $childs); |
---|
1804 | return $ret; |
---|
1805 | } |
---|
1806 | |
---|
1807 | protected function drawLHeadformStart() { |
---|
1808 | $this->drawHeadformStart('j'); |
---|
1809 | } |
---|
1810 | |
---|
1811 | /// Отобразить заголовок шапки документа |
---|
1812 | protected function drawHeadformStart($alt = '') { |
---|
1813 | global $tmpl, $CONFIG, $db; |
---|
1814 | $pref = \pref::getInstance(); |
---|
1815 | if ($this->doc_data['date']) |
---|
1816 | $dt = date("Y-m-d H:i:s", $this->doc_data['date']); |
---|
1817 | else |
---|
1818 | $dt = date("Y-m-d H:i:s"); |
---|
1819 | $tmpl->addContent("<form method='post' action='' id='doc_head_form'> |
---|
1820 | <input type='hidden' name='mode' value='{$alt}heads'> |
---|
1821 | <input type='hidden' name='type' value='" . $this->doc_type . "'>"); |
---|
1822 | if (isset($this->doc_data['id'])) |
---|
1823 | $tmpl->addContent("<input type='hidden' name='doc' value='" . $this->doc_data['id'] . "'>"); |
---|
1824 | if (@$this->doc_data['mark_del']) |
---|
1825 | $tmpl->addContent("<h3>Документ помечен на удаление!</h3>"); |
---|
1826 | $tmpl->addContent(" |
---|
1827 | <table id='doc_head_main'> |
---|
1828 | <tr><td class='altnum'>А. номер</td><td class='subtype'>Подтип</td><td class='datetime'>Дата и время</td><tr> |
---|
1829 | <tr class='inputs'> |
---|
1830 | <td class='altnum'><input type='text' name='altnum' value='" . $this->doc_data['altnum'] . "' id='anum'><a href='#' onclick=\"return GetValue('/doc.php?mode=incnum&type=" . $this->doc_type . "&doc=" . $this->id . "', 'anum', 'sudata', 'datetime', 'firm_id')\"><img border=0 src='/img/i_add.png' alt='Новый номер'></a></td> |
---|
1831 | <td class='subtype'><input type='text' name='subtype' value='" . $this->doc_data['subtype'] . "' id='sudata'></td> |
---|
1832 | <td class='datetime'><input type='text' name='datetime' value='$dt' id='datetime'></td> |
---|
1833 | </tr> |
---|
1834 | </table> |
---|
1835 | Организация:<br><select name='firm' id='firm_id'>"); |
---|
1836 | $res = $db->query("SELECT `id`, `firm_name` FROM `doc_vars` ORDER BY `firm_name`"); |
---|
1837 | if (!$this->doc_data['firm_id']) |
---|
1838 | $this->doc_data['firm_id'] = $pref->site_default_firm; |
---|
1839 | while ($nx = $res->fetch_row()) { |
---|
1840 | if ($this->doc_data['firm_id'] == $nx[0]) |
---|
1841 | $s = ' selected'; |
---|
1842 | else |
---|
1843 | $s = ''; |
---|
1844 | $tmpl->addContent("<option value='$nx[0]' $s>$nx[1] / $nx[0]</option>"); |
---|
1845 | } |
---|
1846 | $tmpl->addContent("</select><br>"); |
---|
1847 | } |
---|
1848 | |
---|
1849 | protected function drawLHeadformEnd() { |
---|
1850 | global $tmpl; |
---|
1851 | $tmpl->addContent("<br>Комментарий:<br><textarea name='comment'>" . html_out($this->doc_data['comment']) . "</textarea></form>"); |
---|
1852 | } |
---|
1853 | |
---|
1854 | protected function drawHeadformEnd() { |
---|
1855 | global $tmpl; |
---|
1856 | $tmpl->addContent(@"<br>Комментарий:<br><textarea name='comment'>" . html_out($this->doc_data['comment']) . "</textarea><br><input type=submit value='Записать'></form>"); |
---|
1857 | } |
---|
1858 | |
---|
1859 | /// Сформировать поля выбора агента |
---|
1860 | protected function drawAgentField() { |
---|
1861 | global $tmpl, $db; |
---|
1862 | $balance = agentCalcDebt($this->doc_data['agent']); |
---|
1863 | $bonus = docCalcBonus($this->doc_data['agent']); |
---|
1864 | $col = ''; |
---|
1865 | if ($balance > 0) |
---|
1866 | $col = "color: #f00; font-weight: bold;"; |
---|
1867 | if ($balance < 0) |
---|
1868 | $col = "color: #f08; font-weight: bold;"; |
---|
1869 | |
---|
1870 | $res = $db->query("SELECT `doc_list`.`id`, `doc_dopdata`.`value` |
---|
1871 | FROM `doc_list` |
---|
1872 | LEFT JOIN `doc_dopdata` ON `doc_dopdata`.`doc`=`doc_list`.`id` AND `doc_dopdata`.`param`='name' |
---|
1873 | WHERE `agent`='{$this->doc_data['agent']}' AND `type`='14' AND `firm_id`='{$this->doc_data['firm_id']}'"); |
---|
1874 | $contr_content = ''; |
---|
1875 | while ($nxt = $res->fetch_row()) { |
---|
1876 | $selected = ($this->doc_data['contract'] == $nxt[0]) ? 'selected' : ''; |
---|
1877 | $contr_content.="<option value='$nxt[0]' $selected>N$nxt[0]: $nxt[1]</option>"; |
---|
1878 | } |
---|
1879 | if ($contr_content) |
---|
1880 | $contr_content = "Договор:<br><select name='contract'>$contr_content</select>"; |
---|
1881 | |
---|
1882 | if ($this->doc_data['agent_dishonest']) |
---|
1883 | $ag = "<span style='color: #f00; font-weight:bold;'>Был выбран недобросовестный агент!</span>"; |
---|
1884 | else |
---|
1885 | $ag = ''; |
---|
1886 | $tmpl->addContent(" |
---|
1887 | <div> |
---|
1888 | <div style='float: right; $col' id='agent_balance_info' onclick=\"ShowPopupWin('/docs.php?l=inf&mode=srv&opt=dolgi&agent={$this->doc_data['agent']}'); return false;\">$balance / $bonus</div> |
---|
1889 | Агент: |
---|
1890 | <a href='/docs.php?l=agent&mode=srv&opt=ep&pos={$this->doc_data['agent']}' id='ag_edit_link' target='_blank'><img src='/img/i_edit.png'></a> |
---|
1891 | <a href='/docs.php?l=agent&mode=srv&opt=ep' target='_blank'><img src='/img/i_add.png'></a> |
---|
1892 | </div> |
---|
1893 | <input type='hidden' name='agent' id='agent_id' value='{$this->doc_data['agent']}'> |
---|
1894 | <input type='text' id='agent_nm' style='width: 100%;' value='" . html_out($this->doc_data['agent_name']) . "'> |
---|
1895 | $ag |
---|
1896 | <div id='agent_contract'>$contr_content</div> |
---|
1897 | <br> |
---|
1898 | |
---|
1899 | <script type=\"text/javascript\"> |
---|
1900 | $(document).ready(function(){ |
---|
1901 | $(\"#agent_nm\").autocomplete(\"/docs.php\", { |
---|
1902 | delay:300, |
---|
1903 | minChars:1, |
---|
1904 | matchSubset:1, |
---|
1905 | autoFill:false, |
---|
1906 | selectFirst:true, |
---|
1907 | matchContains:1, |
---|
1908 | cacheLength:10, |
---|
1909 | maxItemsToShow:15, |
---|
1910 | formatSelectedItem: function(li) { |
---|
1911 | if(li.querySelector('em').dataset.name) |
---|
1912 | li.selectValue = li.querySelector('em').dataset.name; |
---|
1913 | return li; |
---|
1914 | }, |
---|
1915 | formatItem:agliFormat, |
---|
1916 | onItemSelect:agselectItem, |
---|
1917 | extraParams:{'l':'agent','mode':'srv','opt':'ac'} |
---|
1918 | }); |
---|
1919 | }); |
---|
1920 | |
---|
1921 | function agliFormat (row, i, num) { |
---|
1922 | var result = |
---|
1923 | row[0] + |
---|
1924 | \"<em class='qnt' data-name = '\"+row[4]+\"'> тел . \" + row[2] + \"</em>\"; |
---|
1925 | return result; |
---|
1926 | } |
---|
1927 | |
---|
1928 | function agselectItem(li) { |
---|
1929 | if( li == null ) var sValue = \"Ничего не выбрано!\"; |
---|
1930 | if( !!li.extra ) var sValue = li.extra[0]; |
---|
1931 | else var sValue = li.selectValue; |
---|
1932 | document.getElementById('agent_id').value=sValue; |
---|
1933 | document.getElementById('ag_edit_link').href='/docs.php?l=agent&mode=srv&opt=ep&pos='+sValue; |
---|
1934 | var firm_id_elem = document.getElementById('firm_id'); |
---|
1935 | var firm_id = 0; |
---|
1936 | if(firm_id_elem) { |
---|
1937 | firm_id = firm_id_elem.value; |
---|
1938 | } |
---|
1939 | UpdateContractInfo('{$this->id}',firm_id,sValue); |
---|
1940 | |
---|
1941 | "); |
---|
1942 | if (!$this->id) |
---|
1943 | $tmpl->addContent(" |
---|
1944 | var plat_id=document.getElementById('plat_id'); |
---|
1945 | if(plat_id) plat_id.value=li.extra[0]; |
---|
1946 | var plat=document.getElementById('plat'); |
---|
1947 | if(plat) plat.value=li.selectValue; |
---|
1948 | var gruzop_id=document.getElementById('gruzop_id'); |
---|
1949 | if(gruzop_id) gruzop_id.value=li.extra[0]; |
---|
1950 | var gruzop=document.getElementById('gruzop'); |
---|
1951 | if(gruzop) gruzop.value=li.selectValue;"); |
---|
1952 | $tmpl->addContent(" |
---|
1953 | } |
---|
1954 | </script>"); |
---|
1955 | } |
---|
1956 | |
---|
1957 | protected function drawSkladField() { |
---|
1958 | global $tmpl, $db; |
---|
1959 | $tmpl->addContent("Склад:<br> |
---|
1960 | <select name='sklad'>"); |
---|
1961 | $res = $db->query("SELECT `id`,`name` FROM `doc_sklady` ORDER BY `id`"); |
---|
1962 | |
---|
1963 | while ($nxt = $res->fetch_row()) { |
---|
1964 | if ($nxt[0] == $this->doc_data['sklad']) |
---|
1965 | $tmpl->addContent("<option value='$nxt[0]' selected>" . html_out($nxt[1]) . "</option>"); |
---|
1966 | else |
---|
1967 | $tmpl->addContent("<option value='$nxt[0]'>" . html_out($nxt[1]) . "</option>"); |
---|
1968 | } |
---|
1969 | $tmpl->addContent("</select><br>"); |
---|
1970 | } |
---|
1971 | |
---|
1972 | protected function drawBankField() { |
---|
1973 | global $tmpl, $CONFIG, $db; |
---|
1974 | if ($this->doc_data['firm_id']) |
---|
1975 | $sql_add = "AND ( `firm_id`='0' OR `num`='{$this->doc_data['bank']}' OR `firm_id`='{$this->doc_data['firm_id']}' )"; |
---|
1976 | else |
---|
1977 | $sql_add = ''; |
---|
1978 | if ($this->doc_data['bank']) |
---|
1979 | $bank = $this->doc_data['bank']; |
---|
1980 | else { |
---|
1981 | $pref = \pref::getInstance(); |
---|
1982 | $bank = $pref->getSitePref('default_bank_id'); |
---|
1983 | } |
---|
1984 | $tmpl->addContent("Банк:<br><select name='bank'>"); |
---|
1985 | $res = $db->query("SELECT `num`, `name`, `rs` FROM `doc_kassa` WHERE `ids`='bank' $sql_add ORDER BY `num`"); |
---|
1986 | while ($nxt = $res->fetch_row()) { |
---|
1987 | if ($nxt[0] == $bank) |
---|
1988 | $tmpl->addContent("<option value='$nxt[0]' selected>" . html_out($nxt[1] . ' / ' . $nxt[2]) . "</option>"); |
---|
1989 | else |
---|
1990 | $tmpl->addContent("<option value='$nxt[0]'>" . html_out($nxt[1] . ' / ' . $nxt[2]) . "</option>"); |
---|
1991 | } |
---|
1992 | $tmpl->addContent("</select><br>"); |
---|
1993 | } |
---|
1994 | |
---|
1995 | protected function drawKassaField() { |
---|
1996 | global $tmpl, $db, $CONFIG; |
---|
1997 | if ($this->doc_data['kassa']) { |
---|
1998 | $kassa = $this->doc_data['kassa']; |
---|
1999 | } else { |
---|
2000 | $pref = \pref::getInstance(); |
---|
2001 | $kassa = $pref->getSitePref('default_cash_id'); |
---|
2002 | } |
---|
2003 | settype($kassa, 'int'); |
---|
2004 | $tmpl->addContent("Касса:<br><select name='kassa'>"); |
---|
2005 | $res = $db->query("SELECT `num`, `name` FROM `doc_kassa` WHERE `ids`='kassa' AND |
---|
2006 | (`firm_id`='0' OR `firm_id` IS NULL OR `firm_id`='{$this->doc_data['firm_id']}' OR `num`='$kassa') ORDER BY `num`"); |
---|
2007 | |
---|
2008 | if ($kassa == 0) { |
---|
2009 | $tmpl->addContent("<option value='0'>--не выбрана--</option>"); |
---|
2010 | } |
---|
2011 | while ($nxt = $res->fetch_row()) { |
---|
2012 | if ($nxt[0] == $kassa) { |
---|
2013 | $tmpl->addContent("<option value='$nxt[0]' selected>" . html_out($nxt[1]) . "</option>"); |
---|
2014 | } else { |
---|
2015 | $tmpl->addContent("<option value='$nxt[0]'>" . html_out($nxt[1]) . "</option>"); |
---|
2016 | } |
---|
2017 | } |
---|
2018 | $tmpl->addContent("</select><br>"); |
---|
2019 | } |
---|
2020 | |
---|
2021 | protected function drawSumField() { |
---|
2022 | global $tmpl; |
---|
2023 | $tmpl->addContent("Сумма:<br> |
---|
2024 | <input type='text' name='sum' value='{$this->doc_data['sum']}'><img src='/img/i_+-.png'><br>"); |
---|
2025 | } |
---|
2026 | |
---|
2027 | protected function drawPriceField() { |
---|
2028 | global $tmpl, $db; |
---|
2029 | $tmpl->addContent("Цена:<a onclick='ResetCost(\"{$this->id}\"); return false;' id='reset_cost'><img src='/img/i_reload.png'></a><br> |
---|
2030 | <select name='cena'>"); |
---|
2031 | $s = ''; |
---|
2032 | if ($this->dop_data['cena'] == 0) |
---|
2033 | $s = ' selected'; |
---|
2034 | $tmpl->addContent("<option value='0'{$s}>--авто--</option>"); |
---|
2035 | $res = $db->query("SELECT `id`,`name` FROM `doc_cost` ORDER BY `name`"); |
---|
2036 | while ($nxt = $res->fetch_row()) { |
---|
2037 | if ($this->dop_data['cena'] == $nxt[0]) |
---|
2038 | $s = 'selected'; |
---|
2039 | else |
---|
2040 | $s = ''; |
---|
2041 | $tmpl->addContent("<option value='$nxt[0]' $s>" . html_out($nxt[1]) . "</option>"); |
---|
2042 | } |
---|
2043 | |
---|
2044 | if ($this->doc_data['nds']) |
---|
2045 | $tmpl->addContent("<label><input type='radio' name='nds' value='0' disabled>Выделять НДС</label> |
---|
2046 | <label><input type='radio' name='nds' value='1' checked>Включать НДС</label><br>"); |
---|
2047 | else |
---|
2048 | $tmpl->addContent("<label><input type='radio' name='nds' value='0' checked>Выделять НДС</label> |
---|
2049 | <label><input type='radio' name='nds' value='1'>Включать НДС</label><br>"); |
---|
2050 | $tmpl->addContent("<br>"); |
---|
2051 | } |
---|
2052 | |
---|
2053 | // ====== Получение данных, связанных с документом ============================= |
---|
2054 | protected function get_docdata() { |
---|
2055 | if (isset($this->doc_data)) { |
---|
2056 | return; |
---|
2057 | } |
---|
2058 | global $db; |
---|
2059 | if ($this->id) { |
---|
2060 | $this->loadFromDb($this->id); |
---|
2061 | } else { |
---|
2062 | if (method_exists($this, 'initDefDopData')) { |
---|
2063 | $this->initDefDopData(); |
---|
2064 | } |
---|
2065 | $this->dop_data = $this->def_dop_data; |
---|
2066 | $pref = \pref::getInstance(); |
---|
2067 | |
---|
2068 | $this->doc_data = array('id' => 0, 'type' => '', 'agent' => $pref->getSitePref('default_agent_id'), 'comment' => '', 'date' => time(), 'ok' => 0, |
---|
2069 | 'sklad' => $pref->getSitePref('default_store_id'), 'user' => 0, 'altnum' => 0, 'subtype' => '', 'sum' => 0, 'nds' => 1, 'p_doc' => 0, 'mark_del' => 0, |
---|
2070 | 'kassa' => 0, 'bank' => 0, 'firm_id' => 0, 'contract' => 0, 'created' => 0, 'agent_name' => '', 'agent_fullname' => '', 'agent_dishonest' => 0, 'agent_comment' => ''); |
---|
2071 | |
---|
2072 | if (!$this->doc_data['agent']) { |
---|
2073 | $this->doc_data['agent'] = 1; |
---|
2074 | } |
---|
2075 | $agent_data = $db->selectRow('doc_agent', $this->doc_data['agent']); |
---|
2076 | if (is_array($agent_data)) { |
---|
2077 | $this->doc_data['agent_name'] = $agent_data['name']; |
---|
2078 | } |
---|
2079 | |
---|
2080 | if (!$this->doc_data['sklad']) { |
---|
2081 | $this->doc_data['sklad'] = 1; |
---|
2082 | } |
---|
2083 | } |
---|
2084 | } |
---|
2085 | |
---|
2086 | /// Проверка уникальности альтернативного порядкового номера документа |
---|
2087 | public function isAltNumUnique() { |
---|
2088 | global $db; |
---|
2089 | $start_date = strtotime(date("Y-01-01 00:00:00", $this->doc_data['date'])); |
---|
2090 | $end_date = strtotime(date("Y-12-31 23:59:59", $this->doc_data['date'])); |
---|
2091 | $subtype_sql = $db->real_escape_string($this->doc_data['subtype']); |
---|
2092 | $res = $db->query("SELECT `altnum` FROM `doc_list`" |
---|
2093 | . " WHERE `type`='{$this->doc_type}' AND `altnum`='{$this->doc_data['altnum']}' AND `subtype`='$subtype_sql'" |
---|
2094 | . " AND `id`!='{$this->id}' AND `date`>='$start_date' AND `date`<='$end_date' AND `firm_id`='{$this->doc_data['firm_id']}'"); |
---|
2095 | return $res->num_rows ? false : true; |
---|
2096 | } |
---|
2097 | |
---|
2098 | /// Получение альтернативного порядкового номера документа |
---|
2099 | public function getNextAltNum($doc_type, $subtype, $date, $firm_id) { |
---|
2100 | global $CONFIG, $db; |
---|
2101 | if (!$doc_type) { |
---|
2102 | $doc_type = $this->doc_type; |
---|
2103 | } |
---|
2104 | $start_date = strtotime(date("Y-01-01 00:00:00", strtotime($date))); |
---|
2105 | $end_date = strtotime(date("Y-12-31 23:59:59", strtotime($date))); |
---|
2106 | $res = $db->query("SELECT `altnum` FROM `doc_list` WHERE `type`='$doc_type' AND `subtype`='$subtype'" |
---|
2107 | . " AND `id`!='{$this->id}' AND `date`>='$start_date' AND `date`<='$end_date' AND `firm_id`='$firm_id'" |
---|
2108 | . " ORDER BY `altnum` ASC"); |
---|
2109 | $newnum = 0; |
---|
2110 | while ($nxt = $res->fetch_row()) { |
---|
2111 | if (($nxt[0] - 1 > $newnum) && @$CONFIG['doc']['use_persist_altnum']) |
---|
2112 | break; |
---|
2113 | $newnum = $nxt[0]; |
---|
2114 | } |
---|
2115 | $newnum++; |
---|
2116 | return $newnum; |
---|
2117 | } |
---|
2118 | |
---|
2119 | /// Кнопки меню - провети / отменить |
---|
2120 | protected function getDopButtons() { |
---|
2121 | global $tmpl; |
---|
2122 | $ret = ''; |
---|
2123 | if ($this->id) { |
---|
2124 | $ret.="<a href='/doc.php?mode=log&doc={$this->id}' title='История изменений документа'><img src='img/i_log.png' alt='История'></a>"; |
---|
2125 | $ret.="<span id='provodki'>"; |
---|
2126 | if ($this->doc_data['ok']) { |
---|
2127 | $ret .= $this->getCancelButtons(); |
---|
2128 | } else { |
---|
2129 | $ret .= $this->getApplyButtons(); |
---|
2130 | } |
---|
2131 | |
---|
2132 | $ret .= "</span> |
---|
2133 | <img src='/img/i_separator.png' alt=''> |
---|
2134 | <a href='#' onclick=\"return PrintMenu(event, '{$this->id}')\" title='Печать'> |
---|
2135 | <img src='img/i_print.png' alt='Печать'></a> |
---|
2136 | <a href='#' onclick=\"return FaxMenu(event, '{$this->id}')\" title='Отправить по факсу'> |
---|
2137 | <img src='img/i_fax.png' alt='Факс'></a> |
---|
2138 | <a href='#' onclick=\"return MailMenu(event, '{$this->id}')\" title='Отправить по email'> |
---|
2139 | <img src='img/i_mailsend.png' alt='email'></a> |
---|
2140 | <img src='/img/i_separator.png' alt=''> |
---|
2141 | <a href='#' onclick=\"DocConnect('{$this->id}', '{$this->doc_data['p_doc']}'); return false;\" title='Связать документ'> |
---|
2142 | <img src='img/i_conn.png' alt='Связать'></a> |
---|
2143 | <a href='#' onclick=\"return ShowContextMenu(event, '/doc.php?mode=morphto&doc={$this->id}')\" |
---|
2144 | title='Создать связанный документ'><img src='img/i_to_new.png' alt='Связь'></a>"; |
---|
2145 | if ($this->sklad_editor_enable) { |
---|
2146 | $ret .= " <a href='#' onclick=\"return addNomMenu(event, '{$this->id}', '{$this->doc_data['p_doc']}');\" title='Обновить номенклатурную таблицу'><img src='img/i_addnom.png' alt='Обновить номенклатурную таблицу'></a>"; |
---|
2147 | } |
---|
2148 | $ret.="<img src='/img/i_separator.png' alt=''>"; |
---|
2149 | } |
---|
2150 | |
---|
2151 | if (method_exists($this, 'getAdditionalButtonsHTML')) { |
---|
2152 | $ret .= $this->getAdditionalButtonsHTML(); |
---|
2153 | } |
---|
2154 | return $ret; |
---|
2155 | } |
---|
2156 | |
---|
2157 | protected function getApplyButtons() { |
---|
2158 | if ($this->doc_data['mark_del']) { |
---|
2159 | return "<a href='#' title='Отменить удаление' onclick='unMarkDelDoc({$this->id}); return false;'><img src='img/i_trash_undo.png' alt='отменить удаление'></a>"; |
---|
2160 | } else { |
---|
2161 | return "<a href='#' title='Пометить на удаление' onclick='MarkDelDoc({$this->id}); return false;'><img src='img/i_trash.png' alt='Пометить на удаление'></a>" . |
---|
2162 | "<a href='#' title='Провести документ' onclick='ApplyDoc({$this->id}); return false;'><img src='img/i_ok.png' alt='Провести'></a>"; |
---|
2163 | } |
---|
2164 | //<a href='?mode=ehead&doc={$this->doc}' title='Правка заголовка'><img src='img/i_docedit.png' alt='Правка'></a> |
---|
2165 | } |
---|
2166 | |
---|
2167 | protected function getCancelButtons() { |
---|
2168 | return "<a title='Отменить проводку' onclick='CancelDoc({$this->id}); return false;'><img src='img/i_revert.png' alt='Отменить' /></a>"; |
---|
2169 | } |
---|
2170 | |
---|
2171 | /// Вычисление, можно ли отменить кассовый документ |
---|
2172 | protected function checkKassMinus() { |
---|
2173 | global $db; |
---|
2174 | $sum = $i = 0; |
---|
2175 | $res = $db->query("SELECT `doc_list`.`id`, `doc_list`.`type`, `doc_list`.`sum`, `doc_list`.`kassa` FROM `doc_list` |
---|
2176 | WHERE `doc_list`.`ok`>'0' AND ( `doc_list`.`type`='6' OR `doc_list`.`type`='7' OR `doc_list`.`type`='9') |
---|
2177 | ORDER BY `doc_list`.`date`"); |
---|
2178 | while ($nxt = $res->fetch_row()) { |
---|
2179 | if ($nxt[3] == $this->doc_data['kassa']) { |
---|
2180 | if ($nxt[1] == 6) |
---|
2181 | $sum += $nxt[2]; |
---|
2182 | else if ($nxt[1] == 7 || $nxt[1] == 9) |
---|
2183 | $sum -= $nxt[2]; |
---|
2184 | } |
---|
2185 | else if ($nxt[1] == 9) { |
---|
2186 | $rr = $db->query("SELECT `value` FROM `doc_dopdata` WHERE `doc`='$nxt[0]' AND `param`='v_kassu'"); |
---|
2187 | if (!$rr->num_rows) |
---|
2188 | throw new AutoLoggedException('Касса назначения не найдена в документе ' . $this->id); |
---|
2189 | $data = $rr->fetch_row(); |
---|
2190 | if ($data[0] == $this->doc_data['kassa']) |
---|
2191 | $sum+=$nxt[2]; |
---|
2192 | } |
---|
2193 | |
---|
2194 | $sum = sprintf("%01.2f", $sum); |
---|
2195 | if ($sum < 0) |
---|
2196 | break; |
---|
2197 | $i++; |
---|
2198 | } |
---|
2199 | $res->free(); |
---|
2200 | return $sum; |
---|
2201 | } |
---|
2202 | |
---|
2203 | /// Показать историю изменений документа |
---|
2204 | public function showLog() { |
---|
2205 | global $tmpl; |
---|
2206 | \acl::accessGuard('doc.' . $this->typename, \acl::VIEW); |
---|
2207 | if ($this->doc_data['firm_id'] > 0) { |
---|
2208 | \acl::accessGuard([ 'firm.global', 'firm.' . $this->doc_data['firm_id']], \acl::VIEW); |
---|
2209 | } |
---|
2210 | $tmpl->setTitle($this->viewname . ' N' . $this->id); |
---|
2211 | doc_menu($this->getDopButtons()); |
---|
2212 | $tmpl->addContent("<h1>{$this->viewname} N{$this->id} - история документа</h1>"); |
---|
2213 | |
---|
2214 | $logview = new \LogView(); |
---|
2215 | $logview->setObject('doc'); |
---|
2216 | $logview->setObjectId($this->id); |
---|
2217 | $logview->showLog(); |
---|
2218 | } |
---|
2219 | |
---|
2220 | /// Получить список номенклатуры |
---|
2221 | function getDocumentNomenclature($options = '') { |
---|
2222 | global $CONFIG, $db; |
---|
2223 | $opts = array(); |
---|
2224 | $e_options = explode(',', $options); |
---|
2225 | foreach ($e_options as $opt) { |
---|
2226 | $opts[$opt] = 1; |
---|
2227 | } |
---|
2228 | $fields_sql = $join_sql = ''; |
---|
2229 | if (isset($opts['country'])) { |
---|
2230 | $fields_sql .= ", `class_country`.`name` AS `country_name`, `class_country`.`number_code` AS `country_code`"; |
---|
2231 | $join_sql .= " LEFT JOIN `class_country` ON `class_country`.`id`=`doc_base`.`country`"; |
---|
2232 | } |
---|
2233 | if (isset($opts['comment'])) { |
---|
2234 | $fields_sql .= ", `doc_list_pos`.`comm` AS `comment`"; |
---|
2235 | } |
---|
2236 | if (isset($opts['base_desc'])) { |
---|
2237 | $fields_sql .= ", `doc_base`.`desc` AS `base_desc`"; |
---|
2238 | } |
---|
2239 | if (isset($opts['vat'])) { |
---|
2240 | $fields_sql .= ", `doc_base`.`nds` AS `vat`"; |
---|
2241 | } |
---|
2242 | if (isset($opts['base_price'])) { |
---|
2243 | $fields_sql .= ", `doc_base`.`cost` AS `base_price`"; |
---|
2244 | } |
---|
2245 | if (isset($opts['bulkcnt'])) { |
---|
2246 | $fields_sql .= ", `doc_base`.`bulkcnt`"; |
---|
2247 | } |
---|
2248 | if (isset($opts['dest_place'])) { |
---|
2249 | $to_sklad = (int) $this->dop_data['na_sklad']; |
---|
2250 | $fields_sql .= ", `pt_d`.`mesto` AS `dest_place`"; |
---|
2251 | $join_sql .= " LEFT JOIN `doc_base_cnt` AS `pt_d` ON `pt_d`.`id`=`doc_list_pos`.`tovar` AND `pt_d`.`sklad`='{$to_sklad}'"; |
---|
2252 | } |
---|
2253 | if (isset($opts['bigpack'])) { |
---|
2254 | // ID параметра большой упаковки |
---|
2255 | $res = $db->query("SELECT `id` FROM `doc_base_params` WHERE `codename`='bigpack_cnt'"); |
---|
2256 | if (!$res->num_rows) { |
---|
2257 | $db->query("INSERT INTO `doc_base_params` (`name`, `codename`, `type`, `hidden`)" |
---|
2258 | . " VALUES ('Кол-во в большой упаковке', 'bigpack_cnt', 'int', 0)"); |
---|
2259 | throw new \Exception("Параметр *bigpack_cnt - кол-во в большой упаковке* не найден. Параметр создан."); |
---|
2260 | } |
---|
2261 | list($p_bp_id) = $res->fetch_row(); |
---|
2262 | $fields_sql .= ", `bp_t`.`value` AS `bigpack_cnt`"; |
---|
2263 | $join_sql .= " LEFT JOIN `doc_base_values` AS `bp_t` ON `bp_t`.`id`=`doc_base`.`id` AND `bp_t`.`param_id`='$p_bp_id'"; |
---|
2264 | } |
---|
2265 | if (isset($opts['rto'])) { |
---|
2266 | $fields_sql .= ", `doc_base_dop`.`transit`, `doc_base_dop`.`reserve`, `doc_base_dop`.`offer`"; |
---|
2267 | $join_sql .= " LEFT JOIN `doc_base_dop` ON `doc_base_dop`.`id`=`doc_list_pos`.`tovar`"; |
---|
2268 | } |
---|
2269 | $list = array(); |
---|
2270 | $res = $db->query("SELECT |
---|
2271 | `doc_list_pos`.`tovar` AS `pos_id`, `doc_list_pos`.`cnt`, `doc_list_pos`.`cost` AS `price`, |
---|
2272 | `doc_base`.`vc`, `doc_base`.`name`, `doc_base`.`proizv` AS `vendor`, `doc_base`.`mass`, `doc_base`.`mult`, |
---|
2273 | `doc_group`.`printname` AS `group_printname`, `doc_group`.`id` AS `group_id`, |
---|
2274 | `doc_base_cnt`.`mesto` AS `place`, `doc_base_cnt`.`cnt` AS `base_cnt`, |
---|
2275 | `class_unit`.`rus_name1` AS `unit_name`, `class_unit`.`number_code` AS `unit_code` |
---|
2276 | $fields_sql |
---|
2277 | FROM `doc_list_pos` |
---|
2278 | INNER JOIN `doc_base` ON `doc_list_pos`.`tovar`=`doc_base`.`id` |
---|
2279 | LEFT JOIN `doc_group` ON `doc_group`.`id`=`doc_base`.`group` |
---|
2280 | LEFT JOIN `doc_base_cnt` ON `doc_base_cnt`.`id`=`doc_list_pos`.`tovar` AND `doc_base_cnt`.`sklad`='{$this->doc_data['sklad']}' |
---|
2281 | LEFT JOIN `class_unit` ON `doc_base`.`unit`=`class_unit`.`id` |
---|
2282 | $join_sql |
---|
2283 | WHERE `doc_list_pos`.`doc`='{$this->id}' |
---|
2284 | ORDER BY `doc_list_pos`.`id`"); |
---|
2285 | |
---|
2286 | while ($line = $res->fetch_assoc()) { |
---|
2287 | if ($line['group_printname']) { |
---|
2288 | $line['name'] = $line['group_printname'] . ' ' . $line['name']; |
---|
2289 | } |
---|
2290 | if (!@$CONFIG['doc']['no_print_vendor'] && $line['vendor']) { |
---|
2291 | $line['name'] .= ' / ' . $line['vendor']; |
---|
2292 | } |
---|
2293 | $line['code'] = $line['pos_id']; |
---|
2294 | if ($line['vc']) { |
---|
2295 | $line['code'] .= ' / ' . $line['vc']; |
---|
2296 | } |
---|
2297 | $line['sum'] = $line['price'] * $line['cnt']; |
---|
2298 | |
---|
2299 | if (isset($opts['vat'])) { |
---|
2300 | if($this->firm_vars['param_nds']) { |
---|
2301 | if($line['vat']===null) { |
---|
2302 | $line['vat'] = 0; |
---|
2303 | } |
---|
2304 | $ndsp = $line['vat']; |
---|
2305 | $vat = $ndsp / 100; |
---|
2306 | } |
---|
2307 | else { |
---|
2308 | $ndsp = $vat = 0; |
---|
2309 | } |
---|
2310 | |
---|
2311 | /* |
---|
2312 | $line['price_wo_vat'] = round($line['price'] / (1 + ($line['vat_p'] / 100)), 2); |
---|
2313 | $line['sum_wo_vat'] = $line['price_wo_vat'] * $line['cnt']; |
---|
2314 | $line['vat_s'] = ($line['price'] * $line['cnt']) - $line['sum_wo_vat']; */ |
---|
2315 | $pos = $this->calcVAT($line['price'], $line['cnt'], $vat); |
---|
2316 | $line['price_wo_vat'] = $pos['price']; |
---|
2317 | $line['sum_wo_vat'] = round($pos['sum_wo_vat'], 2); |
---|
2318 | $line['vat_p'] = $ndsp; |
---|
2319 | $line['vat_s'] = round($pos['vat_s'], 2); |
---|
2320 | $line['sum'] = round($pos['sum'], 2); |
---|
2321 | |
---|
2322 | } |
---|
2323 | |
---|
2324 | |
---|
2325 | $list[] = $line; |
---|
2326 | } |
---|
2327 | $res->free(); |
---|
2328 | return $list; |
---|
2329 | } |
---|
2330 | |
---|
2331 | /// Получить список номенклатуры документа с НДС и НТД |
---|
2332 | public function getDocumentNomenclatureWVATandNums() { |
---|
2333 | global $CONFIG, $db; |
---|
2334 | |
---|
2335 | $list = array(); |
---|
2336 | $res = $db->query("SELECT `doc_group`.`printname` AS `group_printname`, `doc_base`.`name`, `doc_base`.`proizv` AS `vendor`, `doc_list_pos`.`cnt`, |
---|
2337 | `doc_list_pos`.`cost`, `doc_list_pos`.`gtd`, `class_country`.`name` AS `country_name`, `doc_base_dop`.`ntd`, |
---|
2338 | `class_unit`.`rus_name1` AS `unit_name`, `doc_list_pos`.`tovar` AS `pos_id`, `class_unit`.`number_code` AS `unit_code`, |
---|
2339 | `class_country`.`number_code` AS `country_code`, `doc_base`.`vc`, `doc_base`.`mass`, `doc_base`.`nds` AS `vat`, `doc_list_pos`.`comm`, |
---|
2340 | `doc_list_pos`.`id` AS `line_id` |
---|
2341 | FROM `doc_list_pos` |
---|
2342 | LEFT JOIN `doc_base` ON `doc_base`.`id`=`doc_list_pos`.`tovar` |
---|
2343 | LEFT JOIN `doc_base_dop` ON `doc_base_dop`.`id`=`doc_list_pos`.`tovar` |
---|
2344 | LEFT JOIN `doc_group` ON `doc_group`.`id`=`doc_base`.`group` |
---|
2345 | LEFT JOIN `class_unit` ON `doc_base`.`unit`=`class_unit`.`id` |
---|
2346 | LEFT JOIN `class_country` ON `class_country`.`id`=`doc_base`.`country` |
---|
2347 | WHERE `doc_list_pos`.`doc`='{$this->id}' |
---|
2348 | ORDER BY `doc_list_pos`.`id`"); |
---|
2349 | |
---|
2350 | while ($nxt = $res->fetch_assoc()) { |
---|
2351 | if($this->firm_vars['param_nds']) { |
---|
2352 | if($nxt['vat']===null) { |
---|
2353 | $nxt['vat'] = 0; |
---|
2354 | } |
---|
2355 | $ndsp = $nxt['vat']; |
---|
2356 | $vat = $ndsp / 100; |
---|
2357 | } |
---|
2358 | else { |
---|
2359 | $ndsp = $vat = 0; |
---|
2360 | } |
---|
2361 | |
---|
2362 | if (!$nxt['country_code']) { |
---|
2363 | //throw new \Exception("Не возможно формирование списка номенклатуры без указания страны происхождения товара"); |
---|
2364 | $nxt['country_code'] = 0; |
---|
2365 | } |
---|
2366 | |
---|
2367 | $pos_name = $nxt['name']; |
---|
2368 | if ($nxt['group_printname']) { |
---|
2369 | $pos_name = $nxt['group_printname'] . ' ' . $pos_name; |
---|
2370 | } |
---|
2371 | if (!@$CONFIG['doc']['no_print_vendor'] && $nxt['vendor']) { |
---|
2372 | $pos_name .= ' / ' . $nxt['vendor']; |
---|
2373 | } |
---|
2374 | $pos_code = $nxt['pos_id']; |
---|
2375 | if ($nxt['vc']) { |
---|
2376 | $pos_code .= ' / ' . $nxt['vc']; |
---|
2377 | } |
---|
2378 | |
---|
2379 | if (@$CONFIG['poseditor']['true_gtd']) { |
---|
2380 | $gtd_array = array(); |
---|
2381 | $gres = $db->query("SELECT `doc_list`.`type`, `doc_list_pos`.`gtd`, `doc_list_pos`.`cnt`, `doc_list`.`id` FROM `doc_list_pos` |
---|
2382 | INNER JOIN `doc_list` ON `doc_list`.`id`=`doc_list_pos`.`doc` |
---|
2383 | WHERE `doc_list_pos`.`tovar`='{$nxt['pos_id']}' AND `doc_list`.`firm_id`='{$this->doc_data['firm_id']}' AND `doc_list`.`type`<='2' |
---|
2384 | AND `doc_list`.`date`<'{$this->doc_data['date']}' AND `doc_list`.`ok`>'0' |
---|
2385 | ORDER BY `doc_list`.`date`"); |
---|
2386 | while ($line = $gres->fetch_assoc()) { |
---|
2387 | if ($line['type'] == 1) { // Поступление |
---|
2388 | $gtd_array[] = array('num' => $line['gtd'], 'cnt' => $line['cnt']); |
---|
2389 | } else { |
---|
2390 | $cnt = $line['cnt']; |
---|
2391 | while ($cnt > 0) { |
---|
2392 | if (count($gtd_array) == 0) { |
---|
2393 | if (\cfg::get('poseditor', 'true_gtd') != 'easy') { |
---|
2394 | throw new \Exception("Не найдены поступления для $cnt единиц товара {$nxt['name']} (для реализации N{$line['id']} в прошлом). Товар был оприходован на другую организацию?"); |
---|
2395 | } else { |
---|
2396 | $gtd_array[] = array('num' => $line['gtd'], 'cnt' => $cnt); |
---|
2397 | } |
---|
2398 | } |
---|
2399 | if ($gtd_array[0]['cnt'] == $cnt) { |
---|
2400 | array_shift($gtd_array); |
---|
2401 | $cnt = 0; |
---|
2402 | } elseif ($gtd_array[0]['cnt'] > $cnt) { |
---|
2403 | $gtd_array[0]['cnt'] -= $cnt; |
---|
2404 | $cnt = 0; |
---|
2405 | } else { |
---|
2406 | $cnt -= $gtd_array[0]['cnt']; |
---|
2407 | array_shift($gtd_array); |
---|
2408 | } |
---|
2409 | } |
---|
2410 | } |
---|
2411 | } |
---|
2412 | |
---|
2413 | $unigtd = array(); |
---|
2414 | $need_cnt = $nxt['cnt']; |
---|
2415 | while ($need_cnt > 0 && count($gtd_array) > 0) { |
---|
2416 | $gtd_num = $gtd_array[0]['num']; |
---|
2417 | $gtd_cnt = $gtd_array[0]['cnt']; |
---|
2418 | if ($gtd_cnt >= $need_cnt) { |
---|
2419 | if (isset($unigtd[$gtd_num])) { |
---|
2420 | $unigtd[$gtd_num] += $need_cnt; |
---|
2421 | } else { |
---|
2422 | $unigtd[$gtd_num] = $need_cnt; |
---|
2423 | } |
---|
2424 | $need_cnt = 0; |
---|
2425 | } else { |
---|
2426 | if (isset($unigtd[$gtd_num])) { |
---|
2427 | $unigtd[$gtd_num] += $gtd_cnt; |
---|
2428 | } else { |
---|
2429 | $unigtd[$gtd_num] = $gtd_cnt; |
---|
2430 | } |
---|
2431 | $need_cnt -= $gtd_cnt; |
---|
2432 | array_shift($gtd_array); |
---|
2433 | } |
---|
2434 | } |
---|
2435 | if ($need_cnt > 0) { |
---|
2436 | if (\cfg::get('poseditor', 'true_gtd') != 'easy') { |
---|
2437 | throw new Exception("Не найдены поступления для $need_cnt единиц товара {$pos_name}. Товар был оприходован на другую организацию?"); |
---|
2438 | } else { |
---|
2439 | $unigtd[' -- '] = $need_cnt; |
---|
2440 | } |
---|
2441 | } |
---|
2442 | foreach ($unigtd as $gtd => $cnt) { |
---|
2443 | $pos = $this->calcVAT($nxt['cost'], $cnt, $vat); |
---|
2444 | $list[] = array( |
---|
2445 | 'line_id' => $nxt['line_id'], |
---|
2446 | 'pos_id' => $nxt['pos_id'], |
---|
2447 | 'code' => $pos_code, |
---|
2448 | 'name' => $pos_name, |
---|
2449 | 'unit_code' => $nxt['unit_code'], |
---|
2450 | 'unit_name' => $nxt['unit_name'], |
---|
2451 | 'cnt' => $cnt, |
---|
2452 | 'price' => $pos['price'], |
---|
2453 | 'orig_price' => $nxt['cost'], |
---|
2454 | 'sum_wo_vat' => round($pos['sum_wo_vat'], 2), |
---|
2455 | 'excise' => 'без акциза', |
---|
2456 | 'vat_p' => $ndsp, |
---|
2457 | 'vat_s' => round($pos['vat_s'], 2), |
---|
2458 | 'sum' => round($pos['sum'], 2), |
---|
2459 | 'country_code' => $nxt['country_code'], |
---|
2460 | 'country_name' => $nxt['country_name'], |
---|
2461 | 'gtd' => $gtd, |
---|
2462 | 'mass' => $nxt['mass'], |
---|
2463 | 'comm' => $nxt['comm'], |
---|
2464 | ); |
---|
2465 | } |
---|
2466 | } else { |
---|
2467 | $pos = $this->calcVAT($nxt['cost'], $nxt['cnt'], $vat); |
---|
2468 | $list[] = array( |
---|
2469 | 'line_id' => $nxt['line_id'], |
---|
2470 | 'pos_id' => $nxt['pos_id'], |
---|
2471 | 'code' => $pos_code, |
---|
2472 | 'name' => $pos_name, |
---|
2473 | 'unit_code' => $nxt['unit_code'], |
---|
2474 | 'unit_name' => $nxt['unit_name'], |
---|
2475 | 'cnt' => $nxt['cnt'], |
---|
2476 | 'price' => $pos['price'], |
---|
2477 | 'orig_price' => $nxt['cost'], |
---|
2478 | 'sum_wo_vat' => round($pos['sum_wo_vat'], 2), |
---|
2479 | 'excise' => 'без акциза', |
---|
2480 | 'vat_p' => $ndsp, |
---|
2481 | 'vat_s' => round($pos['vat_s'], 2), |
---|
2482 | 'sum' => round($pos['sum'], 2), |
---|
2483 | 'country_code' => $nxt['country_code'], |
---|
2484 | 'country_name' => $nxt['country_name'], |
---|
2485 | 'gtd' => $nxt['ntd'], |
---|
2486 | 'mass' => $nxt['mass'], |
---|
2487 | 'comm' => $nxt['comm'], |
---|
2488 | ); |
---|
2489 | } |
---|
2490 | } |
---|
2491 | return $list; |
---|
2492 | } |
---|
2493 | |
---|
2494 | /// Расчет НДС для строки документа |
---|
2495 | /// @param $doc_price Цена единицы товара в документе |
---|
2496 | /// @param $count Количество товара |
---|
2497 | /// @param $vat Ставка НДС |
---|
2498 | protected function calcVAT($doc_price, $count, $vat) { |
---|
2499 | global $CONFIG; |
---|
2500 | if (isset($CONFIG['poseditor']['vat_scheme'])) { |
---|
2501 | $scheme = $CONFIG['poseditor']['vat_scheme']; |
---|
2502 | } else { |
---|
2503 | $scheme = 'correct'; |
---|
2504 | } |
---|
2505 | if ($this->doc_data['nds']) { // НДС включен |
---|
2506 | $pos['sum'] = $doc_price * $count; |
---|
2507 | if ($scheme == '1c') { |
---|
2508 | $pos['sum_wo_vat'] = round($pos['sum'] / (1 + $vat), 2); |
---|
2509 | $pos['vat_s'] = $pos['sum'] - $pos['sum_wo_vat']; |
---|
2510 | $pos['price'] = round($pos['sum_wo_vat'] / $count, 2); |
---|
2511 | } else { |
---|
2512 | $pos['price'] = round($doc_price / (1 + $vat), 2); |
---|
2513 | $pos['sum_wo_vat'] = round($pos['price'] * $count, 2); |
---|
2514 | $pos['vat_s'] = round($doc_price * $count, 2) - $pos['sum_wo_vat']; |
---|
2515 | } |
---|
2516 | } else { |
---|
2517 | $pos['price'] = $pos['price_w_vat'] = $doc_price; |
---|
2518 | $pos['sum_wo_vat'] = round($pos['price'] * $count, 2); |
---|
2519 | $pos['vat_s'] = round($pos['sum_wo_vat'] * $vat, 2); |
---|
2520 | $pos['sum'] = $pos['sum_wo_vat'] + $pos['vat_s']; |
---|
2521 | } |
---|
2522 | return $pos; |
---|
2523 | } |
---|
2524 | |
---|
2525 | /// Установить пометку на удаление у документа |
---|
2526 | protected function serviceDelDoc() { |
---|
2527 | global $db; |
---|
2528 | try { |
---|
2529 | \acl::accessGuard('doc.' . $this->typename, \acl::DELETE); |
---|
2530 | if ($this->doc_data['firm_id'] > 0) { |
---|
2531 | \acl::accessGuard([ 'firm.global', 'firm.' . $this->doc_data['firm_id']], \acl::DELETE); |
---|
2532 | } |
---|
2533 | $tim = time(); |
---|
2534 | |
---|
2535 | $res = $db->query("SELECT `id` FROM `doc_list` WHERE `p_doc`='{$this->id}' AND `mark_del`='0'"); |
---|
2536 | if ($res->num_rows) { |
---|
2537 | throw new Exception("Есть подчинённые не удалённые документы. Удаление невозможно."); |
---|
2538 | } |
---|
2539 | $db->update('doc_list', $this->id, 'mark_del', $tim); |
---|
2540 | doc_log("MARKDELETE", '', "doc", $this->id); |
---|
2541 | $this->doc_data['mark_del'] = $tim; |
---|
2542 | $json = ' { "response": "1", "message": "Пометка на удаление установлена!", "buttons": "' . $this->getApplyButtons() . '", ' |
---|
2543 | . '"statusblock": "Документ помечен на удаление" }'; |
---|
2544 | return $json; |
---|
2545 | } catch (Exception $e) { |
---|
2546 | return "{response: 0, message: '" . $e->getMessage() . "'}"; |
---|
2547 | } |
---|
2548 | } |
---|
2549 | |
---|
2550 | /// Снять пометку на удаление у документа |
---|
2551 | protected function serviceUnDelDoc() { |
---|
2552 | global $db; |
---|
2553 | try { |
---|
2554 | \acl::accessGuard('doc.' . $this->typename, \acl::DELETE); |
---|
2555 | if ($this->doc_data['firm_id'] > 0) { |
---|
2556 | \acl::accessGuard([ 'firm.global', 'firm.' . $this->doc_data['firm_id']], \acl::DELETE); |
---|
2557 | } |
---|
2558 | $db->update('doc_list', $this->id, 'mark_del', 0); |
---|
2559 | doc_log("UNMARKDELETE", '', "doc", $this->id); |
---|
2560 | $json = ' { "response": "1", "message": "Пометка на удаление снята!", "buttons": "' . $this->getApplyButtons() . '", ' |
---|
2561 | . '"statusblock": "Документ не будет удалён" }'; |
---|
2562 | return $json; |
---|
2563 | } catch (Exception $e) { |
---|
2564 | return "{response: 0, message: '" . $e->getMessage() . "'}"; |
---|
2565 | } |
---|
2566 | } |
---|
2567 | |
---|
2568 | /// Экспорт табличной части документа в CSV |
---|
2569 | function CSVExport($to_str = 0) { |
---|
2570 | global $tmpl; |
---|
2571 | $header = "PosNum;ID;VC;Name;Vendor;Cnt;Price;Sum;Comment\r\n"; |
---|
2572 | if (!$to_str) { |
---|
2573 | $tmpl->ajax = 1; |
---|
2574 | header("Content-type: 'application/octet-stream'"); |
---|
2575 | header("Content-Disposition: 'attachment'; filename=predlojenie.csv;"); |
---|
2576 | echo $header; |
---|
2577 | } else { |
---|
2578 | $str_out = $header; |
---|
2579 | } |
---|
2580 | $nomenclature = $this->getDocumentNomenclature('base_desc'); |
---|
2581 | |
---|
2582 | $i = 0; |
---|
2583 | foreach ($nomenclature as $line) { |
---|
2584 | $i++; |
---|
2585 | $str_line = "$i;{$line['pos_id']};\"{$line['vc']}\";\"{$line['name']}\";\"{$line['vendor']}\";{$line['cnt']};{$line['price']};{$line['sum']}\r\n"; |
---|
2586 | if (!$to_str) { |
---|
2587 | echo $str_line; |
---|
2588 | } else { |
---|
2589 | $str_out.=$str_line; |
---|
2590 | } |
---|
2591 | } |
---|
2592 | if ($to_str) { |
---|
2593 | return $str_out; |
---|
2594 | } |
---|
2595 | } |
---|
2596 | |
---|
2597 | /// @brief Создание другого документа на основе текущего |
---|
2598 | /// Метод необходимо переопределить у потомков |
---|
2599 | /// @param $target_type Тип создаваемого документа |
---|
2600 | /// @return Всегда false |
---|
2601 | /// Формирование другого документа на основании текущего |
---|
2602 | function morphTo($target) { |
---|
2603 | global $tmpl, $db; |
---|
2604 | $morphs = $this->getMorphList(); |
---|
2605 | |
---|
2606 | if ($target == '') { |
---|
2607 | $tmpl->ajax = 1; |
---|
2608 | $base_link = "window.location='/doc.php?mode=morphto&doc={$this->id}&tt="; |
---|
2609 | foreach($morphs as $line) { |
---|
2610 | $acl_obj = 'doc.'.$line['document']; |
---|
2611 | if(\acl::testAccess($acl_obj, \acl::CREATE)) { |
---|
2612 | $tmpl->addContent("<div onclick=\"{$base_link}{$line['name']}'\">{$line['viewname']}</div>"); |
---|
2613 | } |
---|
2614 | } |
---|
2615 | } else { |
---|
2616 | $morphs = $this->getMorphList(); |
---|
2617 | $info = null; |
---|
2618 | foreach($morphs as $m_info) { |
---|
2619 | if($m_info['name']===$target) { |
---|
2620 | $info = $m_info; |
---|
2621 | break; |
---|
2622 | } |
---|
2623 | } |
---|
2624 | if(!$info) { |
---|
2625 | throw new \Exception("Неверный код целевого документа."); |
---|
2626 | } |
---|
2627 | |
---|
2628 | \acl::accessGuard('doc.'.$morphs[$target]['document'], \acl::CREATE); |
---|
2629 | $method = 'morphTo_'.$info['name']; |
---|
2630 | if(!method_exists($this, $method)) { |
---|
2631 | throw new \NotFoundException("Метод морфинга не определён."); |
---|
2632 | } |
---|
2633 | $db->startTransaction(); |
---|
2634 | $new_doc = $this->$method($target); |
---|
2635 | $new_doc_id = $new_doc->getId(); |
---|
2636 | $db->commit(); |
---|
2637 | redirect("/doc.php?mode=body&doc=$new_doc_id"); |
---|
2638 | } |
---|
2639 | } |
---|
2640 | |
---|
2641 | /** |
---|
2642 | * Проверка для приходных/расходных кассовых ордеров |
---|
2643 | * и средств из/в банк при проведении документа |
---|
2644 | * @throws Exception При отсутствии |
---|
2645 | */ |
---|
2646 | protected function checkIfTypeForDocumentExists() { |
---|
2647 | $allowedTypes = [ |
---|
2648 | 4 => 'credit_type', |
---|
2649 | 5 => 'rasxodi', |
---|
2650 | 6 => 'credit_type', |
---|
2651 | 7 => 'rasxodi', |
---|
2652 | ]; |
---|
2653 | if (!isset($allowedTypes[$this->doc_type])) { |
---|
2654 | throw new \Exception('Для данного типа документа проверка не разрешена'); |
---|
2655 | } |
---|
2656 | if (cfg::get('doc', 'restrict_dc_nulltype', true) && isset($this->dop_data[$allowedTypes[$this->doc_type]]) && $this->dop_data[$allowedTypes[$this->doc_type]] == 0) { |
---|
2657 | $type = $this->doc_type % 2 === 1 ? 'расхода' : 'дохода'; |
---|
2658 | throw new \Exception("Не задан вид $type у проводимого документа."); |
---|
2659 | } |
---|
2660 | } |
---|
2661 | |
---|
2662 | } |
---|