File Coverage

blib/lib/Slovo/Controller/Poruchki.pm
Criterion Covered Total %
statement 15 93 16.1
branch 0 20 0.0
condition 1 7 14.2
subroutine 4 10 40.0
pod 0 4 0.0
total 20 134 14.9


line stmt bran cond sub pod time code
1             package Slovo::Controller::Poruchki;
2 4     4   59625 use Mojo::Base 'Slovo::Controller', -signatures;
  4         10  
  4         25  
3 4     4   17463 use Mojo::Util qw(dumper decode );
  4         9  
  4         189  
4              
5 4     4   17 use Mojo::JSON qw(true false from_json to_json);
  4         10  
  4         4963  
6              
7             # POST /poruchki
8             # Create and store a new order.
9             # Invoked via OpenAPI by cart.js
10 0     0 0 0 sub store ($c) {
  0         0  
  0         0  
11 0         0 $c->debug('Request body' => Mojo::Util::decode('utf8' => $c->req->body));
12 0 0       0 $c->openapi->valid_input or return;
13              
14             # Why ->{Poruchka}{Poruchka} ????
15 0         0 my $o = $c->validation->output->{Poruchka}{Poruchka};
16 0   0     0 my $order_id = $c->_create_order($o)
17             || return; # error is logged and a message is rendered to the user
18 0 0       0 $c->_create_way_bill($o, $order_id)
19             || return; # error is logged and a message is rendered to the user
20 0         0 $c->debug('poruchka' => $o);
21 0         0 return $c->render(openapi => $o, status => 201);
22             }
23              
24 0     0   0 sub _create_order ($c, $o) {
  0         0  
  0         0  
  0         0  
25              
26 0         0 state $shop = $c->config->{shop};
27 0         0 state $app = $c->app;
28 0         0 my $orders = $c->poruchki;
29              
30             # POST to Econt to create order
31 0         0 my $_order_struct = $c->_order_struct($o);
32             my $eco_res = $app->ua->request_timeout(5)->post(
33             $shop->{crupdate_order_endpoint} =>
34             {'Content-Type' => 'application/json', Authorization => $shop->{private_key}},
35 0         0 json => $_order_struct
36             )->res;
37             $c->debug(
38             'req_url: ' => $shop->{crupdate_order_endpoint},
39 0         0 ' $_order_struct:' => $_order_struct
40             );
41 0         0 $c->debug('$eco_res->json:' => $eco_res->json);
42 0 0       0 if ($eco_res->is_success) {
43 0         0 $o->{deliverer_id} = $eco_res->json->{id} + 0;
44 0         0 $o->{created_at} = $o->{tstamp} = time;
45              
46             # Store in our database
47             # TODO: Implement control panel for orders, invoices, products
48             my $id = $orders->add({
49             poruchka => to_json($o),
50 0         0 map { $_ => $o->{$_} }
  0         0  
51             qw(deliverer_id deliverer name email phone city_name created_at tstamp)
52             });
53 0         0 return $id;
54             }
55              
56             # Something is wrong if we get to here. Log the error and inform the user
57             # that something is wrong with the communication between us and Econt.
58 0         0 $app->log->error('Error from _create_order($c,$o): Econt Status:'
59             . $eco_res->code
60             . $/
61             . 'Econt Response:'
62             . decode(utf8 => $eco_res->body)
63             . $/
64             . __FILE__ . ':'
65             . __LINE__);
66              
67 0         0 $c->render(
68             openapi => {
69             errors => [{
70             path => $c->url_for . '',
71             message => 'Изпращането на поръчката към доставчика се провали.'
72             . $/
73             . 'Състояние: '
74             . $eco_res->code
75             . $/
76             . 'Опитваме се да се поправим. Извинете за неудобството.'
77             }]
78             },
79             status => 418
80             );
81              
82 0         0 return;
83             }
84              
85 0     0   0 sub _create_way_bill ($c, $o, $id) {
  0         0  
  0         0  
  0         0  
  0         0  
86              
87 0         0 state $shop = $c->config->{shop};
88 0         0 state $app = $c->app;
89 0         0 my $orders = $c->poruchki;
90              
91             # $c->debug('Poruchka:' => $o);
92             # POST to Econt to create order
93 0         0 my $_order_struct = $c->_order_struct($o);
94             my $eco_res = $c->app->ua->request_timeout(5)->post(
95             $shop->{create_awb_endpoint} =>
96             {'Content-Type' => 'application/json', Authorization => $shop->{private_key}},
97 0         0 json => $_order_struct
98             )->res;
99             $c->debug(
100             'req_url: ' => $shop->{create_awb_endpoint},
101 0         0 ' $_order_struct:' => $_order_struct
102             );
103 0         0 my $way_bill = $eco_res->json;
104 0         0 $c->debug('товарителница $eco_res->json:' => $way_bill);
105 0 0       0 if ($eco_res->is_success) {
106 0         0 $o->{tstamp} = time;
107 0         0 $o->{way_bill_id} = $way_bill->{shipmentNumber};
108              
109             # update in our database
110             # TODO: Implement control panel for orders, invoices, products
111             $orders->save(
112             $id,
113             {
114             way_bill => to_json($way_bill),
115             poruchka => to_json($o),
116 0         0 map { $_ => $o->{$_} } qw(tstamp way_bill_id)
  0         0  
117             },
118              
119             );
120 0         0 return 1;
121             }
122              
123 0         0 $app->log->error('Error from _create_way_bill($c,$o): Econt Status:'
124             . $eco_res->code
125             . $/
126             . 'Econt Response:'
127             . decode(utf8 => $eco_res->body)
128             . $/
129             . __FILE__ . ':'
130             . __LINE__);
131              
132 0         0 $c->render(
133             openapi => {
134             errors => [{
135             path => $c->url_for . '',
136             message => "Създаването на товарителница се провали, но поръчката ви "
137             . "($o->{deliverer_id}) е приета."
138             . " Ще се свържем с вас на предоствения от вас телефон."
139             . $/
140             . 'Състояние: '
141             . $eco_res->code
142             . $/
143             . 'Опитваме се да се поправим. Извинете за неудобството.'
144             }]
145             },
146             status => 418
147             );
148              
149 0         0 return;
150             }
151              
152             # Returns a structure for JSON body for a create/update query and a way bill (товарителница) to Econt.
153             # First time this is passed without id to create one at deliverer site.
154             # Second time the deliverer_id is passed as id.
155             # 1. $shop->{crupdate_order_endpoint}
156             # 2. $shop->{create_awb_endpoint}
157 0     0   0 sub _order_struct ($c, $o) {
  0         0  
  0         0  
  0         0  
158 0         0 my $items = $o->{items};
159             return {
160              
161             ($o->{deliverer_id} ? (id => $o->{deliverer_id}) : ()),
162              
163             #id => $o->{id},
164             #orderNumber => $o->{id},
165             cod => 1,
166              
167             # NOTE!!! TODO: Implement automatic Invoice creation from order!
168             # Which field is for "invoice_num" as reported in the error from Econt???
169             declaredValue => $o->{sum},
170             currency => $o->{shipping_price_currency},
171              
172             # TODO: implement product types as started in table products column type.
173             shipmentDescription => (
174             'книг' . (@$items > 1 ? 'и' : 'а') . ' ISBN: ' . join ';',
175 0         0 map {"$_->{sku}: $_->{quantity}бр."} @$items
176             ),
177             receiverShareAmount => $o->{shipping_price_cod} || 0,
178             customerInfo => {
179             name => $o->{name},
180             face => $o->{face},
181             phone => $o->{phone},
182             email => $o->{email},
183             countryCode => $o->{id_country},
184             cityName => $o->{city_name},
185             postCode => $o->{post_code},
186             officeCode => $o->{office_code},
187             address => ($o->{office_code} ? "" : $o->{address}),
188             quarter => $o->{quarter},
189             street => $o->{street},
190             num => $o->{num},
191             other => $o->{other},
192             },
193             items => [
194 0 0 0     0 map { {
    0          
    0          
195             name => $_->{title},
196             SKU => $_->{sku},
197             count => $_->{quantity},
198             hideCount => 0,
199             totalPrice => ($_->{quantity} * $_->{price}),
200 0         0 totalWeight => ($_->{quantity} * $_->{weight}),
201             } } @$items
202             ]};
203             }
204              
205             # GET /poruchka/:deliverer/:id
206             # show an order by given :deliverer and :id with that deliverer.
207             # Invoked via OpenAPI by cart.js
208 0     0 0 0 sub show ($c) {
  0         0  
  0         0  
209 0 0       0 $c->openapi->valid_input or return;
210 0         0 my $deliverer = $c->param('deliverer');
211 0         0 my $deliverer_id = $c->param('deliverer_id');
212              
213             # Initially generated checksum by the econt order form. Only a user having
214             # the order in his localStorage has it. If the user clears it's cache in
215             # the browser, the order and this checksum is lost. The 'id' query
216             # parameter is to prevent from brute force guessing.
217 0         0 my $id = $c->param('id');
218              
219 0         0 my $order = $c->poruchki->find_where({
220             deliverer => $deliverer,
221             deliverer_id => $deliverer_id,
222             poruchka => {-like => qq|%"id":"$id"%|}});
223              
224 0 0       0 return $c->render(
225             openapi => {errors => [{path => $c->url_for . '', message => 'Not Found'}]},
226             status => 404
227             ) unless $order;
228              
229             # TODO: check for changes on econt side each two ours or more. If there is
230             # a way_bill_id, store the updated order and show it to the user.
231              
232 0         0 $order->{poruchka} = from_json($order->{poruchka});
233 0         0 return $c->render(openapi => $order->{poruchka});
234             }
235              
236             # GET /api/shop
237             # provides shipment data to the page on which the form for shipping the
238             # collected goods in the cart is called.
239 0     0 0 0 sub shop ($c) {
  0         0  
  0         0  
240              
241             # TODO: some logic to use the right shop. We may have multiple
242             # shops(physical stores) from which we send the orders. For example we may
243             # choose the shop depending on the IP-location of the user. We want to use
244             # the closest store to the user to minimise delivery expenses.
245              
246             # Copy data without private_key.
247 0 0       0 state $shop = {map { $_ eq 'private_key' ? () : ($_ => $c->config->{shop}{$_}) }
248 0         0 keys %{$c->config->{shop}}};
  0         0  
249 0         0 return $c->render(openapi => $shop);
250             }
251              
252             # GET /api/gdpr_consent
253             # Provides various settings to the client side like the url for the page where
254             # are described the detailed conditions to use the site and the cookies policy
255             # - GDPR.
256 1     1 0 30873 sub consents ($c) {
  1         3  
  1         1  
257 1         6 state $consents = $c->config('consents');
258 1   33     25 $consents->{ihost} //= $c->ihost_only;
259 1         66 return $c->render(openapi => $consents);
260             }
261              
262             1;
263