Issues (92)

src/sale/SaleRepository.php (1 issue)

1
<?php
2
/**
3
 * API for Billing
4
 *
5
 * @link      https://github.com/hiqdev/billing-hiapi
6
 * @package   billing-hiapi
7
 * @license   BSD-3-Clause
8
 * @copyright Copyright (c) 2017-2018, HiQDev (http://hiqdev.com/)
9
 */
10
11
namespace hiqdev\billing\hiapi\sale;
12
13
use hiqdev\php\billing\action\ActionInterface;
14
use hiqdev\php\billing\customer\CustomerInterface;
15
use hiqdev\php\billing\order\OrderInterface;
16
use hiqdev\php\billing\plan\PlanInterface;
17
use hiqdev\php\billing\sale\Sale;
18
use hiqdev\php\billing\sale\SaleInterface;
19
use hiqdev\php\billing\sale\SaleRepositoryInterface;
20
use hiqdev\yii\DataMapper\expressions\CallExpression;
21
use hiqdev\yii\DataMapper\expressions\HstoreExpression;
22
use hiqdev\yii\DataMapper\models\relations\Bucket;
23
use hiqdev\yii\DataMapper\query\Specification;
24
use hiqdev\yii\DataMapper\repositories\BaseRepository;
25
use yii\db\Query;
26
27
class SaleRepository extends BaseRepository implements SaleRepositoryInterface
28
{
29
    /** {@inheritdoc} */
30
    public $queryClass = SaleQuery::class;
31
32
    public function findId(SaleInterface $sale)
33
    {
34
        if ($sale->hasId()) {
35
            return $sale->getId();
36
        }
37
        $hstore = new HstoreExpression(array_filter([
38
            'buyer'     => $sale->getCustomer()->getLogin(),
39
            'buyer_id'  => $sale->getCustomer()->getId(),
40
            'object_id' => $sale->getTarget()->getId(),
41
            'tariff_id' => $sale->getPlan()->getId(),
42
        ], static function ($value): bool {
43
            return $value !== null;
44
        }, ARRAY_FILTER_USE_BOTH));
45
        $call = new CallExpression('sale_id', [$hstore]);
46
        $command = (new Query())->select($call);
47
48
        return $command->scalar($this->db);
49
    }
50
51
    /**
52
     * @param OrderInterface $order
53
     * @return Sale[]|SaleInterface[]
54
     */
55
    public function findByOrder(OrderInterface $order)
56
    {
57
        return array_map([$this, 'findByAction'], $order->getActions());
58
    }
59
60
    /**
61
     * Used to find a sale by action target.
62
     *
63
     * @param ActionInterface $action
64
     * @return SaleInterface|false
65
     */
66
    public function findByAction(ActionInterface $action)
67
    {
68
        $type = $action->getTarget()->getType();
69
        if ($type === null) {
70
            // When action target type is not set, then action can be applied to any target.
71
            // It means we can not find exact sale, so return null.
72
            // Used at lest for:
73
            //  - temporary actions, when in-memory action is matched against an in-memory plan.
74
            return false;
75
        }
76
77
        if ($type === 'certificate') {
78
            $target_id = new CallExpression('class_id', ['certificate']);
79
        } elseif ($type === 'domain' || $type === 'feature') {
80
            $target_id = new CallExpression('class_id', ['zone']);
81
        } elseif ($type === 'server' || $type === 'device') {
82
            $target_id = $action->getTarget()->getId();
83
        } elseif ($type === 'serverConfig') {
84
            $target_id = $action->getTarget()->getId();
85
        } elseif ($type === 'part') {
86
            // Crutch. Actions for specific parts are currently used at least in
87
            // - DeviceMonthlyEstimateMux
88
            //
89
            // For regular billing, all actions are created for the whole server
90
            // but have type monthly,hardware.
91
            return false;
92
        } else {
93
            throw new \Exception('not implemented for: ' . $type);
94
        }
95
96
        $spec = $this->createSpecification()
97
            /// XXX how to pass if we want with prices into joinPlans?
98
            ->with('plans')
99
            ->where($this->buildTargetCond($target_id, $action->getCustomer()));
100
101
        return $this->findOne($spec);
102
    }
103
104
    protected function buildTargetCond($target_id, CustomerInterface $buyer)
105
    {
106
        $condition = ['target-id' => $target_id];
107
108
        $client_id = $buyer->getId();
109
        if ($client_id) {
110
            $condition['customer-id'] = $client_id;
111
            $condition['seller-id_ne'] = $client_id;
112
        } else {
113
            $condition['customer-id'] = $condition['seller-id'] = $buyer->getSeller()->getId();
114
        }
115
116
        return $condition;
117
    }
118
119
    protected function joinPlans(&$rows)
120
    {
121
        $bucket = Bucket::fromRows($rows, 'plan-id');
122
        $spec = $this->createSpecification()
123
            ->with('prices')
124
            ->where(['id' => $bucket->getKeys()]);
125
        $raw_plans = $this->getRepository(PlanInterface::class)->queryAll($spec);
126
        $bucket->fill($raw_plans, 'id');
127
        $bucket->pourOneToOne($rows, 'plan');
128
    }
129
130
    /**
131
     * @param SaleInterface $sale
132
     */
133
    public function save(SaleInterface $sale)
134
    {
135
        $call = new CallExpression('sale_object', [$this->prepareHstore($sale)]);
136
        $command = (new Query())->select($call);
137
        $sale->setId($command->scalar($this->db));
138
    }
139
140
    public function delete(SaleInterface $sale)
141
    {
142
        $call = new CallExpression('unsale_object', [$this->prepareHstore($sale)]);
143
        $command = (new Query())->select($call);
144
        $command->scalar($this->db);
145
    }
146
147
    private function prepareHstore(SaleInterface $sale)
148
    {
149
        $time = $sale->getTime();
150
151
        return new HstoreExpression([
152
            'object_id'     => $sale->getTarget()->getId(),
153
            'contact_id'    => $sale->getCustomer()->getId(),
154
            'tariff_id'     => $sale->getPlan() ? $sale->getPlan()->getId() : null,
155
            'time'          => $time ? $time->format('c') : null,
0 ignored issues
show
$time is of type DateTimeImmutable, thus it always evaluated to true.
Loading history...
156
        ]);
157
    }
158
}
159