File Coverage

blib/lib/Slovo/Controller/Poruchki.pm
Criterion Covered Total %
statement 13 87 14.9
branch 0 18 0.0
condition 0 5 0.0
subroutine 4 10 40.0
pod 0 4 0.0
total 17 124 13.7


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