Sale.php 26 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060
  1. <?php
  2. /**
  3. *
  4. * PHP Pro Bid $Id$ rdCz8wCfwhKMXDq32otN36Gbk9AT9Mf4dC4bhC9kKE4=
  5. *
  6. * @link http://www.phpprobid.com
  7. * @copyright Copyright (c) 2017 Online Ventures Software & CodeCube SRL
  8. * @license http://www.phpprobid.com/license Commercial License
  9. *
  10. * @version 7.9 [rev.7.9.02]
  11. */
  12. /**
  13. * sales table row object model
  14. */
  15. namespace Ppb\Db\Table\Row;
  16. use Ppb\Db\Table\SalesListings,
  17. Ppb\Db\Table\Vouchers as VouchersTable,
  18. Cube\Db\Expr,
  19. Ppb\Service;
  20. class Sale extends AbstractRow
  21. {
  22. /**
  23. * sale payment statuses
  24. */
  25. const PAYMENT_UNPAID = 0;
  26. const PAYMENT_PAID = 1;
  27. const PAYMENT_PAID_DIRECT_PAYMENT = 2;
  28. const PAYMENT_PAY_ARRIVAL = 3;
  29. /**
  30. * sale shipping statuses
  31. */
  32. const SHIPPING_PROCESSING = 0;
  33. const SHIPPING_SENT = 1;
  34. const SHIPPING_PROBLEM = 2;
  35. const SHIPPING_NA = -1;
  36. /**
  37. *
  38. * payment statuses array
  39. *
  40. * @var array
  41. */
  42. public static $paymentStatuses = array(
  43. self::PAYMENT_UNPAID => 'Unpaid',
  44. self::PAYMENT_PAID => 'Paid',
  45. self::PAYMENT_PAID_DIRECT_PAYMENT => 'Paid (Direct Payment)',
  46. self::PAYMENT_PAY_ARRIVAL => 'Payment on Arrival',
  47. );
  48. /**
  49. *
  50. * shipping statuses array
  51. *
  52. * @var array
  53. */
  54. public static $shippingStatuses = array(
  55. self::SHIPPING_PROCESSING => 'Processing',
  56. self::SHIPPING_SENT => 'Posted/Sent',
  57. self::SHIPPING_PROBLEM => 'Problem',
  58. self::SHIPPING_NA => 'N/A',
  59. );
  60. /**
  61. *
  62. * sale data array keys
  63. *
  64. * @var array
  65. */
  66. protected static $saleDataKeys = array(
  67. 'currency',
  68. 'country',
  69. 'state',
  70. 'address',
  71. 'pickup_options'
  72. );
  73. /**
  74. *
  75. * sales listings rowset
  76. * (used to override the sales listings table in order to preview a sale total)
  77. *
  78. * @var array
  79. */
  80. protected $_salesListings = array();
  81. /**
  82. *
  83. * sales service
  84. *
  85. * @var \Ppb\Service\Sales
  86. */
  87. protected $_sales;
  88. /**
  89. *
  90. * serializable fields
  91. *
  92. * @var array
  93. */
  94. protected $_serializable = array('sale_data');
  95. /**
  96. *
  97. * the tax amount that corresponds to the sale
  98. * calculated by the calculateTotal() method
  99. *
  100. * @var bool|float
  101. */
  102. protected $_taxAmount = false;
  103. /**
  104. *
  105. * voucher object to be applied
  106. *
  107. * @var \Ppb\Db\Table\Row\Voucher|null
  108. */
  109. protected $_voucher = false;
  110. /**
  111. *
  112. * get sales listings array / rowset
  113. *
  114. * @return array|\Ppb\Db\Table\Rowset\SalesListings
  115. */
  116. public function getSalesListings()
  117. {
  118. if (empty($this->_salesListings)) {
  119. $this->setSalesListings(
  120. $this->findDependentRowset('\Ppb\Db\Table\SalesListings'));
  121. }
  122. return $this->_salesListings;
  123. }
  124. /**
  125. *
  126. * set sales listings array / rowset
  127. *
  128. * @param array|\Ppb\Db\Table\Rowset\SalesListings $salesListings
  129. *
  130. * @return $this
  131. */
  132. public function setSalesListings($salesListings)
  133. {
  134. $this->clearSalesListings();
  135. foreach ($salesListings as $saleListing) {
  136. $this->addSaleListing($saleListing);
  137. }
  138. return $this;
  139. }
  140. /**
  141. *
  142. * clear sales listings array
  143. *
  144. * @return $this
  145. */
  146. public function clearSalesListings()
  147. {
  148. $this->_salesListings = array();
  149. return $this;
  150. }
  151. /**
  152. *
  153. * add sale listing row
  154. *
  155. * @param array|\Ppb\Db\Table\Row\SaleListing $saleListing
  156. *
  157. * @return $this
  158. * @throws \InvalidArgumentException
  159. */
  160. public function addSaleListing($saleListing)
  161. {
  162. if ($saleListing instanceof SaleListing) {
  163. $this->_salesListings[] = $saleListing;
  164. }
  165. else {
  166. if (!array_key_exists('price', $saleListing) ||
  167. !array_key_exists('quantity', $saleListing)
  168. ) {
  169. throw new \InvalidArgumentException("The sale listing array must include the 'price' and 'quantity' keys");
  170. }
  171. $this->_salesListings[] = new SaleListing(array(
  172. 'data' => $saleListing,
  173. 'table' => new SalesListings(),
  174. ));
  175. }
  176. return $this;
  177. }
  178. /**
  179. *
  180. * set tax amount
  181. *
  182. * @param bool|float $taxAmount
  183. *
  184. * @return $this
  185. */
  186. public function setTaxAmount($taxAmount)
  187. {
  188. $this->_taxAmount = floatval($taxAmount);
  189. return $this;
  190. }
  191. /**
  192. *
  193. * get tax amount - calculate it if unset
  194. *
  195. * @param bool $force force tax calculation
  196. *
  197. * @return bool|float
  198. */
  199. public function getTaxAmount($force = false)
  200. {
  201. if ($this->_taxAmount === false || $force === true) {
  202. $this->calculateTotal();
  203. }
  204. return $this->_taxAmount;
  205. }
  206. /**
  207. * @param null|\Ppb\Db\Table\Row\Voucher $voucher
  208. */
  209. public function setVoucher($voucher)
  210. {
  211. $this->_voucher = $voucher;
  212. }
  213. /**
  214. * @return null|\Ppb\Db\Table\Row\Voucher
  215. */
  216. public function getVoucher()
  217. {
  218. if ($this->_voucher === false) {
  219. if (($data = \Ppb\Utility::unserialize($this->getData('voucher_details'))) !== false) {
  220. $voucher = new Voucher(array(
  221. 'table' => new VouchersTable(),
  222. 'data' => $data,
  223. ));
  224. }
  225. else {
  226. $voucher = null;
  227. }
  228. $this->setVoucher($voucher);
  229. }
  230. return $this->_voucher;
  231. }
  232. /**
  233. *
  234. * the method will calculate the total value of the sale invoice, including postage and insurance amounts
  235. * "postage_amount" and "insurance_amount" will always be calculated when the sale row is created
  236. * the total is calculated based on the "tax_rate" field and tax applies to the total amount
  237. *
  238. * @param bool $simple
  239. * @param bool $applyVoucher we can opt to not apply the voucher when calculating the total price (for example on the initial cart page)
  240. *
  241. * @return float
  242. */
  243. public function calculateTotal($simple = false, $applyVoucher = true)
  244. {
  245. $result = 0;
  246. $taxAmount = null;
  247. $salesListings = $this->getSalesListings();
  248. $applyVoucher = ($this->getData('pending') && $applyVoucher) ? true : false;
  249. foreach ($salesListings as $saleListing) {
  250. /** @var \Ppb\Db\Table\Row\SaleListing $saleListing */
  251. $result += $saleListing->calculateTotal($applyVoucher);
  252. }
  253. if ($simple === false) {
  254. if ($this->hasPostage()) {
  255. $result += $this->getData('postage_amount');
  256. if ($this->getInsuranceAmount()) {
  257. $result += $this->getData('insurance_amount');
  258. }
  259. }
  260. if ($this->getData('tax_rate') > 0) {
  261. $taxAmount = $result * $this->getData('tax_rate') / 100;
  262. $result += $taxAmount;
  263. }
  264. }
  265. $this->setTaxAmount($taxAmount);
  266. return $result;
  267. }
  268. /**
  269. *
  270. * get the active status of a sale invoice
  271. * 7.8: by default if a sale is under force payment effect, we treat it as inactive except for the possibility for the
  272. * buyer to make payment or for the seller to update payment status
  273. *
  274. * @param bool $expiresAt
  275. *
  276. * @return bool
  277. */
  278. public function isActive($expiresAt = true)
  279. {
  280. $active = ($this->getData('active') && !$this->getData('pending')) ? true : false;
  281. return ($expiresAt && $this->getData('expires_at')) ? false : $active;
  282. }
  283. /**
  284. *
  285. * checks if the sale is marked as paid
  286. *
  287. * @return bool
  288. */
  289. public function isPaid()
  290. {
  291. return ($this->getData('flag_payment') == self::PAYMENT_UNPAID) ? false : true;
  292. }
  293. /**
  294. *
  295. * whether an invoice can be edited/combined
  296. * if the logged in user is the buyer, we add an additional flag to check if the sale is locked for editing
  297. * (as in only the seller can edit it)
  298. *
  299. * @return bool
  300. */
  301. public function canEdit()
  302. {
  303. if (!count($this)) {
  304. return false;
  305. }
  306. $canEdit = (
  307. $this->isActive() &&
  308. !$this->isPaid() &&
  309. $this->getData('buyer_id') &&
  310. $this->getData('seller_id')
  311. ) ? true : false;
  312. $user = $this->getUser();
  313. if ($user['id'] === $this->getData('buyer_id') && $this->getData('edit_locked')) {
  314. $canEdit = false;
  315. }
  316. return $canEdit;
  317. }
  318. /**
  319. *
  320. * check if the invoice can be deleted
  321. *
  322. * @return bool
  323. */
  324. public function canDelete()
  325. {
  326. if (!count($this)) {
  327. return false;
  328. }
  329. $canDelete = (
  330. !$this->getData('expires_at')
  331. ) ? true : false;
  332. return $canDelete;
  333. }
  334. /**
  335. *
  336. * check if the active user can combine invoices
  337. * invoices cannot be combined if a voucher has been applied on one of them
  338. *
  339. * @return bool
  340. */
  341. public function canCombinePurchases()
  342. {
  343. if (!$this->getData('voucher_details')) {
  344. $settings = $this->getSettings();
  345. if ($this->isSeller() || $settings['buyer_create_invoices']) {
  346. return true;
  347. }
  348. }
  349. return false;
  350. }
  351. /**
  352. *
  353. * check if the logged in user is the seller in the sale transaction
  354. *
  355. * @param bool $admin check if the logged in user is an admin as well
  356. *
  357. * @return bool
  358. */
  359. public function isSeller($admin = false)
  360. {
  361. $user = $this->getUser();
  362. $result = false;
  363. if ($this->getData('seller_id') == $user['id']) {
  364. $result = true;
  365. }
  366. else if ($admin == true && $user['role'] == 'Admin') {
  367. $result = true;
  368. }
  369. return $result;
  370. }
  371. /**
  372. *
  373. * check if the logged in user is the buyer in the sale transaction
  374. *
  375. * @return bool
  376. */
  377. public function isBuyer()
  378. {
  379. $user = $this->getUser();
  380. if ($this->getData('buyer_id') == $user['id']) {
  381. return true;
  382. }
  383. return false;
  384. }
  385. /**
  386. *
  387. * get the pickup option that applies to this sale
  388. *
  389. * @return string|null
  390. */
  391. public function getPickupOptions()
  392. {
  393. /** @var \Ppb\Db\Table\Rowset\SalesListings $salesListings */
  394. $salesListings = $this->findDependentRowset('\Ppb\Db\Table\SalesListings');
  395. /** @var \Ppb\Db\Table\Row\SaleListing $saleListing */
  396. foreach ($salesListings as $saleListing) {
  397. /** @var \Ppb\Db\Table\Row\Listing $listing */
  398. $listing = $saleListing->findParentRow('\Ppb\Db\Table\Listings');
  399. return $listing[\Ppb\Model\Shipping::FLD_PICKUP_OPTIONS];
  400. }
  401. return null;
  402. }
  403. /**
  404. *
  405. * get the direct payment methods that apply to this sale
  406. *
  407. * @param string $type type of payment methods to retrieve ('direct', 'offline' or null for all)
  408. *
  409. * @return array
  410. */
  411. public function getPaymentMethods($type = null)
  412. {
  413. $paymentMethods = null;
  414. /** @var \Ppb\Db\Table\Rowset\SalesListings $salesListings */
  415. $salesListings = $this->findDependentRowset('\Ppb\Db\Table\SalesListings');
  416. /** @var \Ppb\Db\Table\Row\SaleListing $saleListing */
  417. foreach ($salesListings as $saleListing) {
  418. /** @var \Ppb\Db\Table\Row\Listing $listing */
  419. $listing = $saleListing->findParentRow('\Ppb\Db\Table\Listings');
  420. if ($listing instanceof Listing) {
  421. $listingPaymentMethods = $listing->getPaymentMethods($type);
  422. if ($paymentMethods === null) {
  423. $paymentMethods = $listingPaymentMethods;
  424. }
  425. else {
  426. $paymentMethods = array_uintersect($paymentMethods, $listingPaymentMethods,
  427. function ($a, $b) {
  428. return strcmp($a['name'] . $a['type'], $b['name'] . $b['type']);
  429. });
  430. }
  431. }
  432. }
  433. return $paymentMethods;
  434. }
  435. /**
  436. *
  437. * return true if direct payment can be made for the sale by the logged in user (the buyer)
  438. *
  439. * @return array|false an array of direct payment methods or false if payment is not possible
  440. */
  441. public function canPayDirectPayment()
  442. {
  443. $user = $this->getUser();
  444. if ($this->isActive(false) &&
  445. $user['id'] == $this->getData('buyer_id') &&
  446. !$this->isPaid()
  447. ) {
  448. $paymentMethods = $this->getPaymentMethods('direct');
  449. if (count($paymentMethods) > 0) {
  450. return $paymentMethods;
  451. }
  452. }
  453. return false;
  454. }
  455. /**
  456. *
  457. * set expires at flag
  458. * used by the force payment function
  459. *
  460. * 7.8: if not all sale listings are products, disable this function
  461. *
  462. * @param bool $reset
  463. *
  464. * @return $this
  465. */
  466. public function setExpiresFlag($reset = false)
  467. {
  468. $settings = $this->getSettings();
  469. $salesListings = $this->findDependentRowset('\Ppb\Db\Table\SalesListings');
  470. /** @var \Ppb\Db\Table\Row\SaleListing $saleListing */
  471. foreach ($salesListings as $saleListing) {
  472. /** @var \Ppb\Db\Table\Row\Listing $listing */
  473. $listing = $saleListing->findParentRow('\Ppb\Db\Table\Listings');
  474. if ($listing->getData('listing_type') != 'product') {
  475. $reset = true;
  476. break;
  477. }
  478. }
  479. if ($reset) {
  480. $flag = new Expr('null');
  481. }
  482. else {
  483. $flag = new Expr('now() + interval ' . $settings['force_payment_limit'] . ' minute');
  484. }
  485. $this->save(array(
  486. 'expires_at' => $flag,
  487. ));
  488. $this->_data['expires_at'] = ($reset) ? null : date('Y-m-d H:i:s', time() + $settings['force_payment_limit'] * 60);
  489. return $this;
  490. }
  491. /**
  492. *
  493. * get the insurance amount that will apply to the sale
  494. *
  495. * @return float|null
  496. */
  497. public function getInsuranceAmount()
  498. {
  499. if ($this->getData('apply_insurance')) {
  500. return $this->getData('insurance_amount');
  501. }
  502. return null;
  503. }
  504. /**
  505. *
  506. * check if postage is available for the selected sale
  507. * this flag is independent on the enable shipping global setting, to preserve the postage amount
  508. * in case the global setting was changed
  509. *
  510. * @return bool
  511. */
  512. public function hasPostage()
  513. {
  514. if ($this->getData('enable_shipping')) {
  515. return true;
  516. }
  517. if ($this->getData('pending')) {
  518. $salesListings = $this->getSalesListings();
  519. /** @var \Ppb\Db\Table\Row\SaleListing $saleListing */
  520. foreach ($salesListings as $saleListing) {
  521. /** @var \Ppb\Db\Table\Row\Listing $listing */
  522. $listing = $saleListing->findParentRow('\Ppb\Db\Table\Listings');
  523. if (!$listing->isShipping()) {
  524. return false;
  525. }
  526. }
  527. return true;
  528. }
  529. return false;
  530. }
  531. /**
  532. *
  533. * get the postage method for the sale
  534. *
  535. * @return string
  536. */
  537. public function getPostageMethod()
  538. {
  539. if (isset($this->_data['postage']['method'])) {
  540. return $this->_data['postage']['method'];
  541. }
  542. return 'N/A';
  543. }
  544. /**
  545. *
  546. * get payment status description
  547. *
  548. * @return string
  549. */
  550. public function getPaymentStatusDescription()
  551. {
  552. if (array_key_exists($this->_data['flag_payment'], self::$paymentStatuses)) {
  553. return self::$paymentStatuses[$this->_data['flag_payment']];
  554. }
  555. return 'N/A';
  556. }
  557. /**
  558. *
  559. * get the sale transaction row object or null if no transaction exists
  560. *
  561. * @return \Cube\Db\Table\Row|null
  562. */
  563. public function getSaleTransaction()
  564. {
  565. $select = $this->getTable()->select()
  566. ->where('paid = ?', 1);
  567. $transaction = $this->findDependentRowset('\Ppb\Db\Table\Transactions', null, $select)->getRow(0);
  568. return $transaction;
  569. }
  570. /**
  571. *
  572. * function that checks if the sale invoice can be viewed
  573. * only the seller or the buyer can view a sale invoice
  574. *
  575. * @return bool
  576. */
  577. public function canView()
  578. {
  579. if (!count($this) || !$this->isActive()) {
  580. return false;
  581. }
  582. $user = $this->getUser();
  583. if (in_array($user['id'], array($this->getData('seller_id'), $this->getData('buyer_id')))) {
  584. return true;
  585. }
  586. return false;
  587. }
  588. /**
  589. *
  590. * checks if the messaging feature is enabled for the sale
  591. *
  592. * @return bool
  593. */
  594. public function messagingEnabled()
  595. {
  596. $settings = $this->getSettings();
  597. if ($settings['enable_messaging']) {
  598. return true;
  599. }
  600. return false;
  601. }
  602. /**
  603. *
  604. * create a link to the messaging controller
  605. * if we have a topic, redirect to the topic, otherwise create a new topic
  606. *
  607. * @return array
  608. */
  609. public function messagingLink()
  610. {
  611. $action = ($this->getData('messaging_topic_id')) ? 'topic' : 'create';
  612. $array = array(
  613. 'module' => 'members',
  614. 'controller' => 'messaging',
  615. 'action' => $action,
  616. );
  617. if ($action == 'topic') {
  618. $array['id'] = $this->getData('messaging_topic_id');
  619. }
  620. else {
  621. $array['sale_id'] = $this->getData('id');
  622. $array['topic_type'] = Service\Messaging::SALE_TRANSACTION;
  623. }
  624. return $array;
  625. }
  626. /**
  627. *
  628. * create a link to the post reputation form
  629. * will include all listings in the sale transaction that have pending feedback
  630. *
  631. * @param int $posterId
  632. *
  633. * @return array
  634. */
  635. public function reputationLink($posterId)
  636. {
  637. $array = array();
  638. $reputationService = new Service\Reputation();
  639. $salesListings = $this->findDependentRowset('\Ppb\Db\Table\SalesListings');
  640. $salesListingsIds = array(0);
  641. foreach ($salesListings as $saleListing) {
  642. $salesListingsIds[] = $saleListing['id'];
  643. }
  644. $select = $reputationService->getTable()->select()
  645. ->where('poster_id = ?', $posterId)
  646. ->where('posted = ?', 0)
  647. ->where('sale_listing_id IN (?)', $salesListingsIds);
  648. $reputations = $reputationService->fetchAll($select);
  649. $ids = array();
  650. foreach ($reputations as $reputation) {
  651. $ids[] = $reputation['id'];
  652. }
  653. if (count($ids) > 0) {
  654. $array = array(
  655. 'module' => 'members',
  656. 'controller' => 'reputation',
  657. 'action' => 'post',
  658. 'id' => $ids,
  659. );
  660. }
  661. return $array;
  662. }
  663. /**
  664. *
  665. * check if the buyer can checkout the cart
  666. * - logged in user needs to be different than the seller
  667. * - check for available inventory
  668. * - if shipping is enabled, check if the item shipping to the buyer's selected address
  669. * - we need to have a shopping cart
  670. *
  671. * @return bool|string true if ok, a message otherwise
  672. */
  673. public function canCheckout()
  674. {
  675. $translate = $this->getTranslate();
  676. $user = $this->getUser();
  677. $userId = (!empty($user['id'])) ? $user['id'] : null;
  678. if (!$this->getData('pending')) {
  679. return sprintf($translate->_('Invalid shopping cart selected.'));
  680. }
  681. else if ($userId == $this->getData('seller_id')) {
  682. return sprintf($translate->_('You cannot purchase your own products.'));
  683. }
  684. return true;
  685. }
  686. /**
  687. *
  688. * checks whether the logged in user can pay sale fee or not
  689. *
  690. * @return bool
  691. */
  692. public function canPayFee()
  693. {
  694. if ($this->isActive(false)) {
  695. return false;
  696. }
  697. $settings = $this->getSettings();
  698. $user = $this->getUser();
  699. switch ($settings['sale_fee_payer']) {
  700. case 'buyer':
  701. if ($user['id'] == $this->getData('buyer_id')) {
  702. return true;
  703. }
  704. break;
  705. case 'seller':
  706. if ($user['id'] == $this->getData('seller_id')) {
  707. return true;
  708. }
  709. break;
  710. }
  711. return false;
  712. }
  713. /**
  714. *
  715. * determine and set the 'active' flag for the sale
  716. *
  717. * @param int $active
  718. *
  719. * @return $this
  720. */
  721. public function updateActive($active = 1)
  722. {
  723. $this->save(array(
  724. 'active' => (int)$active,
  725. ));
  726. return $this;
  727. }
  728. /**
  729. *
  730. * update payment and shipping statuses for the sale object
  731. * also reset expires at flag if payment status is marked as paid - one way action
  732. *
  733. * @param array $post
  734. *
  735. * @return $this
  736. */
  737. public function updateStatus($post)
  738. {
  739. $saleData = (array)$this->getData('sale_data');
  740. $saleData['tracking_link'] = $post['tracking_link'];
  741. $this->saveSaleData(array(
  742. 'tracking_link' => $post['tracking_link'],
  743. ));
  744. $data = array(
  745. 'flag_shipping' => $post['flag_shipping'],
  746. 'flag_payment' => $post['flag_payment'],
  747. );
  748. if ($post['flag_payment'] != self::PAYMENT_UNPAID) {
  749. $this->setExpiresFlag(true);
  750. }
  751. parent::save($data);
  752. return $this;
  753. }
  754. /**
  755. *
  756. * save sale data serialized field
  757. *
  758. * @param array $post
  759. *
  760. * @return $this
  761. */
  762. public function saveSaleData($post)
  763. {
  764. $saleData = array_merge((array)\Ppb\Utility::unserialize($this->getData('sale_data')), $post);
  765. foreach (self::$saleDataKeys as $key) {
  766. if (!array_key_exists($key, $saleData)) {
  767. $saleData[$key] = null;
  768. }
  769. }
  770. $salesListings = $this->findDependentRowset('\Ppb\Db\Table\SalesListings');
  771. $enableShipping = false;
  772. foreach ($salesListings as $saleListing) {
  773. /** @var \Ppb\Db\Table\Row\Listing $listing */
  774. $listing = $saleListing->findParentRow('\Ppb\Db\Table\Listings');
  775. if ($listing->isShipping()) {
  776. $enableShipping = true;
  777. break;
  778. }
  779. }
  780. $saleData['enable_shipping'] = $enableShipping;
  781. $data = array(
  782. 'sale_data' => serialize($saleData),
  783. );
  784. parent::save($data);
  785. return $this;
  786. }
  787. /**
  788. *
  789. * save serialized voucher details
  790. *
  791. * @param string|\Ppb\Db\Table\Row\Voucher $voucher voucher code or voucher object
  792. *
  793. * @return $this
  794. */
  795. public function saveVoucherDetails($voucher)
  796. {
  797. if (!$voucher instanceof Voucher) {
  798. $vouchersService = new Service\Vouchers();
  799. $voucher = $vouchersService->findBy($voucher, $this->getData('seller_id'));
  800. }
  801. if ($voucher instanceof Voucher) {
  802. $voucher = ($voucher->isValid()) ? serialize($voucher->getData()) : null;
  803. }
  804. $this->save(array(
  805. 'voucher_details' => $voucher
  806. ));
  807. return $this;
  808. }
  809. /**
  810. *
  811. * update sales listings quantities
  812. * doesnt check for available quantity - this needs to be checked in the form
  813. * it is also checked in the canCheckout method
  814. *
  815. * @param array $quantities
  816. *
  817. * @return $this
  818. */
  819. public function updateQuantities(array $quantities)
  820. {
  821. $salesListings = $this->getSalesListings();
  822. /** @var \Ppb\Db\Table\Row\SaleListing $saleListing */
  823. foreach ($salesListings as $saleListing) {
  824. $key = $saleListing['id'];
  825. if (array_key_exists($key, $quantities)) {
  826. $quantity = ($quantities[$key] > 1) ? $quantities[$key] : 1;
  827. $saleListing->save(array(
  828. 'quantity' => $quantity,
  829. ));
  830. }
  831. }
  832. return $this;
  833. }
  834. /**
  835. *
  836. * revert the sale then delete it
  837. * the following actions will be taken:
  838. * - the quantities of the listings will be reset
  839. * - the sale transaction fee will be refunded (if payer in account mode)
  840. * - delete related reputation table rows
  841. * - TODO: if listings were closed ahead of time - reopen them
  842. *
  843. * @return $this
  844. */
  845. public function revert()
  846. {
  847. // revert quantities for each listing in the sale
  848. $salesListings = $this->getSalesListings();
  849. /** @var \Ppb\Db\Table\Row\SaleListing $saleListing */
  850. foreach ($salesListings as $saleListing) {
  851. /** @var \Ppb\Db\Table\Row\Listing $listing */
  852. $listing = $saleListing->findParentRow('\Ppb\Db\Table\Listings');
  853. if ($listing instanceof Listing) {
  854. $listing->updateQuantity($saleListing->getData('quantity'), $saleListing['product_attributes'], Listing::ADD);
  855. }
  856. // delete the reputation rows for each sale listing in the sale
  857. $saleListing->findDependentRowset('\Ppb\Db\Table\Reputation')->delete();
  858. }
  859. // refund sale transaction fee(s)
  860. $accountingRowset = $this->findDependentRowset('\Ppb\Db\Table\Accounting');
  861. /** @var \Ppb\Db\Table\Row\Accounting $accounting */
  862. foreach ($accountingRowset as $accounting) {
  863. $accounting->acceptRefundRequest(true);
  864. }
  865. $this->delete(true);
  866. return $this;
  867. }
  868. /**
  869. *
  870. * mark deleted if user is buyer or seller, or remove from database if admin
  871. *
  872. * @param bool $admin
  873. *
  874. * @return int|bool
  875. */
  876. public function delete($admin = false)
  877. {
  878. if ($admin === true) {
  879. return parent::delete();
  880. }
  881. $user = $this->getUser();
  882. if ($user['id'] == $this->getData('seller_id')) {
  883. $this->save(array(
  884. 'seller_deleted' => 1
  885. ));
  886. return true;
  887. }
  888. else if ($user['id'] == $this->getData('buyer_id')) {
  889. $this->save(array(
  890. 'buyer_deleted' => 1
  891. ));
  892. return true;
  893. }
  894. return false;
  895. }
  896. }