Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for multiple currencies #18

Draft
wants to merge 78 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
78 commits
Select commit Hold shift + click to select a range
caa8db9
Added currency model
mrcage Apr 30, 2022
1ac74a7
Configure currency for product
mrcage Apr 30, 2022
abaf38f
Customer currencies migration
mrcage Apr 30, 2022
617d7ef
Show customer balance
mrcage May 1, 2022
ce50fd5
Merge branch 'main' into currencies
mrcage May 1, 2022
8dcb90f
WIP customer currency
mrcage May 1, 2022
656c4f6
WIP
mrcage May 2, 2022
60c004e
WIP
mrcage May 2, 2022
a9c1272
Manage currencies
mrcage May 2, 2022
855a27a
Currency nav permissions
mrcage May 2, 2022
313d94a
Update info on customer account page
mrcage May 2, 2022
4c66881
Show currency on front page
mrcage May 2, 2022
2168e33
Product price styling
mrcage May 2, 2022
d7c4418
Formatting of price
mrcage May 2, 2022
a1100d4
css
mrcage May 2, 2022
8c46980
Added order manager service
mrcage May 2, 2022
2b1555f
Rename quantityAvailableForCustomer()
mrcage May 2, 2022
f350653
rename getQuantityAvailableForOrdering
mrcage May 2, 2022
93edf84
Rename getAvailableQuantityPerOrder
mrcage May 2, 2022
5e9f502
Add customer getBalance method
mrcage May 2, 2022
f1fd369
Balance calc
mrcage May 3, 2022
95c406a
Account balance calc in store
mrcage May 3, 2022
9d7198f
Added progress bar
mrcage May 3, 2022
effd09e
margin
mrcage May 3, 2022
8a1e2fe
Larger number
mrcage May 3, 2022
3b0f43a
fix checkout calculation
mrcage May 3, 2022
781e5b6
Fix checkout button
mrcage May 3, 2022
aa2efe2
Remove product from basket
mrcage May 3, 2022
5653b95
Merge branch 'currencies' of https://github.com/OHFLesvos/free-shop i…
mrcage May 3, 2022
a3abd1d
Fix price check for ordering
mrcage May 3, 2022
27b62bb
Button titles
mrcage May 3, 2022
e647245
Remove checkout page, integrate into shop front
mrcage May 3, 2022
1d96ef5
fix use
mrcage May 3, 2022
e1178dc
WIP send order
mrcage May 4, 2022
64f8688
Fix return type
mrcage May 4, 2022
6ca9a8f
Move DTO
mrcage May 4, 2022
a39ce17
Order costs
mrcage May 4, 2022
e9fc72a
Move customer orders to separate component
mrcage May 4, 2022
0cbf485
Separate customer history component
mrcage May 4, 2022
3f90ae5
Add customer comments components
mrcage May 4, 2022
61b505c
Separate customer tags component
mrcage May 4, 2022
0c7e108
Tabs on customer page
mrcage May 4, 2022
cf821e7
Separate components on order detail page
mrcage May 4, 2022
55dc8c9
Change buttons
mrcage May 4, 2022
3009b80
Order status restyling
mrcage May 4, 2022
0a653b1
border style
mrcage May 4, 2022
6e8a5ba
Fix order registration
mrcage May 4, 2022
c4b956b
Order reg frmat
mrcage May 4, 2022
79d30bb
Formatting
mrcage May 4, 2022
ec0cfcc
Use register order
mrcage May 4, 2022
bdf6c74
cleanup
mrcage May 4, 2022
ef9adb2
Cleanup
mrcage May 4, 2022
395f439
cleanup
mrcage May 4, 2022
a5f0f74
Error handling
mrcage May 4, 2022
6a8bc12
import
mrcage May 4, 2022
38c8026
Added test case
mrcage May 5, 2022
532162b
Remove settings dependency from constructor
mrcage May 5, 2022
5e660fb
Testing
mrcage May 5, 2022
6b54ab7
more test
mrcage May 5, 2022
5cc0915
Balance check before registering order
mrcage May 5, 2022
e31a0e0
Throw InsufficientBalanceException
mrcage May 5, 2022
785b7fe
Ignorewords
mrcage May 5, 2022
9554baa
ignore words
mrcage May 5, 2022
6f37c02
cleanup code and tests
mrcage May 5, 2022
fc78d7b
added more tests
mrcage May 5, 2022
0c01730
Added order ignore costs option
mrcage May 5, 2022
d9eabb3
Add order modify action
mrcage May 5, 2022
c0b5682
Cleanup order edit
mrcage May 6, 2022
e65e91b
Customer detail info restructuring
mrcage May 6, 2022
4cd07b2
redirect to comments
mrcage May 6, 2022
0cab0a1
Merge branch 'main' into currencies
mrcage May 30, 2022
deb37f5
Merge branch 'main' into currencies
mrcage Jun 8, 2022
dad247d
Merge branch 'currencies' of https://github.com/OHFLesvos/free-shop i…
mrcage Jun 15, 2022
579d129
Merge branch 'pint' into currencies
mrcage Jun 28, 2022
65d2f95
run pint
mrcage Jun 28, 2022
71f3bcc
Merge branch 'main' into currencies
mrcage Jun 28, 2022
eb38570
Merge branch 'main' into currencies
mrcage Jun 28, 2022
38c5812
updated ignorewords
mrcage Jun 28, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 13 additions & 6 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,24 +11,31 @@
},
"intelephense.environment.phpVersion": "8.0.0",
"cSpell.ignoreWords": [
"alpinejs",
"fontawesome",
"fortawesome",
"yaireo",
"Authenticatable",
"Authorizable",
"Cviebrock",
"Dyrynda",
"Gumlet",
"Laravel",
"Livewire",
"Lorisleiva",
"Maatwebsite",
"Propaganistas",
"Sluggable",
"Spatie",
"Stringable",
"Torann",
"Twilio",
"alpinejs",
"commentable",
"donatj",
"fontawesome",
"fortawesome",
"geoblock",
"libphonenumber",
"taggables"
],
"livewire",
"sluggable",
"taggables",
"yaireo"
]
}
26 changes: 26 additions & 0 deletions app/Actions/BaseCancelOrder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

namespace App\Actions;

use App\Dto\CostsDto;
use App\Models\Order;
use Lorisleiva\Actions\Concerns\AsAction;

abstract class BaseCancelOrder
{
use AsAction;

public function handle(Order $order): void
{
$order->status = 'cancelled';
$order->save();

if ($order->customer != null) {
$order->getCosts()->each(fn (CostsDto $costs) => $order->customer->addBalance($costs->currency_id, $costs->value));
}

$this->after($order);
}

abstract protected function after(Order $order): void;
}
34 changes: 13 additions & 21 deletions app/Actions/CancelOrder.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,21 @@
namespace App\Actions;

use App\Models\Order;
use Lorisleiva\Actions\Concerns\AsAction;
use Illuminate\Support\Facades\Log;

class CancelOrder
class CancelOrder extends BaseCancelOrder
{
use AsAction;

public function handle(Order $order): void
{
$order->status = 'cancelled';
$order->save();

if ($order->customer != null) {
$startingCredit = setting()->get('customer.starting_credit', config('shop.customer.starting_credit'));
$maximum = setting()->get('customer.credit_top_up.maximum', $startingCredit);
$order->customer->credit = max($order->customer->credit, min($order->customer->credit + $order->costs, $maximum));
$order->customer->save();
}

$this->notify($order);
}

protected function notify(Order $order): void
protected function after(Order $order): void
{
// NOOP
Log::info('Customer cancelled order.', [
'event.kind' => 'event',
'event.outcome' => 'success',
'customer.name' => $order->customer?->name,
'customer.id_number' => $order->customer?->id_number,
'customer.phone' => $order->customer?->phone,
'customer.balance' => $order->customer?->totalBalance(),
'order.id' => $order->id,
'order.costs' => $order->getCostsString(),
]);
}
}
41 changes: 41 additions & 0 deletions app/Actions/ModifyOrder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

namespace App\Actions;

use App\Models\Order;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use Lorisleiva\Actions\Concerns\AsAction;

class ModifyOrder
{
use AsAction;

/**
* @param Order $order
* @param Collection<int,int> $items
* @return void
*/
public function handle(
Order $order,
Collection $items,
): void {

// TODO: Check customer balance

$order->assignProducts($items);

// TODO: Update customer balance

Log::info('Updated order.', [
'event.kind' => 'event',
'event.outcome' => 'success',
'customer.name' => $order->customer?->name,
'customer.id_number' => $order->customer?->id_number,
'customer.phone' => $order->customer?->phone,
'customer.balance' => $order->customer?->totalBalance(),
'order.id' => $order->id,
'order.costs' => $order->getCostsString(),
]);
}
}
136 changes: 136 additions & 0 deletions app/Actions/RegisterOrder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
<?php

namespace App\Actions;

use App\Dto\CostsDto;
use App\Exceptions\EmptyOrderException;
use App\Exceptions\InsufficientBalanceException;
use App\Exceptions\PhoneNumberBlockedByAdminException;
use App\Models\Customer;
use App\Models\Order;
use App\Models\Product;
use App\Notifications\OrderRegistered;
use Exception;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use Lorisleiva\Actions\Concerns\AsAction;

class RegisterOrder
{
use AsAction;

/**
* @param Customer $customer
* @param Collection<int,int> $items
* @param ?string $remarks
* @return Order
*
* @throws EmptyOrderException
* @throws InsufficientBalanceException
*/
public function handle(
Customer $customer,
Collection $items,
?string $remarks = null,
string $logMessage = 'An order has been registered.',
bool $notifyCustomer = false,
bool $ignoreCosts = false,
): Order {
$this->ensureAnyItemsDefined($items);
if (! $ignoreCosts) {
$this->checkBalance($customer, $items);
}

// TODO: Check product availability

$order = $this->createOrder($customer, $remarks);
$order->assignProducts($items);
if (! $ignoreCosts) {
$this->subtractCosts($customer, $order);
}

Log::info($logMessage, [
'event.kind' => 'event',
'event.outcome' => 'success',
'customer.name' => $customer->name,
'customer.id_number' => $customer->id_number,
'customer.phone' => $customer->phone,
'customer.balance' => $customer->totalBalance(),
'order.id' => $order->id,
'order.costs' => $order->getCostsString(),
]);

if ($notifyCustomer) {
$this->notifyCustomer($customer, $order);
}

return $order;
}

/**
* @param Collection<int,int> $items
*/
private function ensureAnyItemsDefined(Collection $items)
{
$items = $items->filter(fn ($quantity) => is_numeric($quantity) && $quantity > 0);
if ($items->isEmpty()) {
throw new EmptyOrderException(__('No products have been selected.'));
}
}

/**
* @param Customer $customer
* @param Collection<int,int> $items
* @return void
*/
private function checkBalance(Customer $customer, Collection $items): void
{
foreach ($customer->currencies as $currency) {
$basketCosts = (int) $items
->map(function (int $quantity, int $productId) use ($currency) {
$product = Product::find($productId);

return ($product->currency_id == $currency->id) ? $product->price * $quantity : 0;
})
->sum();
$balance = $customer->getBalance($currency);
if ($balance < $basketCosts) {
throw new InsufficientBalanceException(__('Insufficient account balance. :required :currency required, but only :available available.', [
'currency' => $currency->name,
'required' => $basketCosts,
'available' => $balance,
]));
}
}
}

private function createOrder(Customer $customer, ?string $remarks): Order
{
$order = new Order();
$order->fill([
'remarks' => $remarks,
'ip_address' => request()->ip(),
'user_agent' => request()->userAgent(),
]);
$order->customer()->associate($customer);
$order->save();

return $order;
}

private function subtractCosts(Customer $customer, Order $order)
{
$order->getCosts()->each(fn (CostsDto $costs) => $customer->subtractBalance($costs->currency_id, $costs->value));
}

private function notifyCustomer(Customer $customer, Order $order)
{
try {
$customer->notify(new OrderRegistered($order));
} catch (PhoneNumberBlockedByAdminException $ex) {
Log::warning("The phone number {$ex->getPhone()} has been blocked by an administrator.");
} catch (Exception $ex) {
Log::warning('['.get_class($ex).'] Cannot send notification: '.$ex->getMessage());
}
}
}
4 changes: 2 additions & 2 deletions app/Actions/RejectOrder.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
use App\Models\Order;
use App\Notifications\OrderCancelled;

class RejectOrder extends CancelOrder
class RejectOrder extends BaseCancelOrder
{
protected function notify(Order $order): void
protected function after(Order $order): void
{
if ($order->customer != null) {
$order->customer->notify(new OrderCancelled($order));
Expand Down
Loading