diff --git a/Website/htdocs/mpmanager/app/Console/Commands/MailApplianceDebtsCommand.php b/Website/htdocs/mpmanager/app/Console/Commands/MailApplianceDebtsCommand.php new file mode 100644 index 000000000..0df08c905 --- /dev/null +++ b/Website/htdocs/mpmanager/app/Console/Commands/MailApplianceDebtsCommand.php @@ -0,0 +1,23 @@ +queryOutstandingDebtsByApplianceRates()->count(); + // do not send mail if there is no customer with appliance debt + if ($applianceDebtHavingCustomerCount > 0) { + $outstandingDebtsExportService->sendApplianceDebtsAsEmail(); + } + } +} diff --git a/Website/htdocs/mpmanager/app/Console/Kernel.php b/Website/htdocs/mpmanager/app/Console/Kernel.php index 89b4cf898..b9d198b21 100644 --- a/Website/htdocs/mpmanager/app/Console/Kernel.php +++ b/Website/htdocs/mpmanager/app/Console/Kernel.php @@ -2,6 +2,7 @@ namespace App\Console; +use app\Console\Commands\MailApplianceDebtsCommand; use Illuminate\Console\Scheduling\Schedule; use Illuminate\Foundation\Console\Kernel as ConsoleKernel; use Inensus\CalinMeter\Console\Commands\InstallPackage as InstallCalinMeterPackage; @@ -68,6 +69,8 @@ protected function schedule(Schedule $schedule) $schedule->command('dummy:create-data 25 --company-id=11 --type=transaction')->dailyAt('00:00'); $schedule->command('dummy:create-data 2 --company-id=11 --type=ticket')->dailyAt('00:00'); $schedule->command('asset-rate:check')->dailyAt('00:00'); + // will run on the last day of the month + $schedule->command(MailApplianceDebtsCommand::class)->weeklyOn(1, '6:00'); } /** diff --git a/Website/htdocs/mpmanager/app/Http/Controllers/OutstandingDebtsExportController.php b/Website/htdocs/mpmanager/app/Http/Controllers/OutstandingDebtsExportController.php index dfc524d31..714e625ef 100644 --- a/Website/htdocs/mpmanager/app/Http/Controllers/OutstandingDebtsExportController.php +++ b/Website/htdocs/mpmanager/app/Http/Controllers/OutstandingDebtsExportController.php @@ -1,34 +1,24 @@ applianceRateService->getOutstandingDebtsByApplianceRates(); - $this->outstandingDebtsExportService->createSpreadSheetFromTemplate($this->outstandingDebtsExportService->getTemplatePath()); - $currency = $this->applianceRateService->getCurrencyFromMainSettings(); - $this->outstandingDebtsExportService->setCurrency($currency); - $this->outstandingDebtsExportService->setOutstandingDebtsData($data); - $this->outstandingDebtsExportService->setExportingData(); - $this->outstandingDebtsExportService->writeOutstandingDebtsData(); - $this->outstandingDebtsExportService->saveSpreadSheet($this->path); + public function download(): BinaryFileResponse + { + $path = $this->outstandingDebtsExportService->createReport(CarbonImmutable::now()); - return response()->download($this->path . '/' . - $this->outstandingDebtsExportService->getRecentlyCreatedSpreadSheetId() . '.xlsx'); + return response()->download($path); } } diff --git a/Website/htdocs/mpmanager/app/Http/Controllers/TransactionExportController.php b/Website/htdocs/mpmanager/app/Http/Controllers/TransactionExportController.php index 7ce4cb24c..41402fa8d 100644 --- a/Website/htdocs/mpmanager/app/Http/Controllers/TransactionExportController.php +++ b/Website/htdocs/mpmanager/app/Http/Controllers/TransactionExportController.php @@ -5,10 +5,10 @@ use Illuminate\Http\Request; use MPM\Transaction\Export\TransactionExportService; use MPM\Transaction\TransactionService; +use Symfony\Component\HttpFoundation\BinaryFileResponse; class TransactionExportController { - private string $path = __DIR__ . '/../../modules/Transaction/Export'; public function __construct( private TransactionService $transactionService, private TransactionExportService $transactionExportService @@ -17,7 +17,7 @@ public function __construct( public function download( Request $request, - ) { + ): BinaryFileResponse { $type = $request->get('deviceType') ?: 'meter'; $serialNumber = $request->get('serial_number'); $tariffId = $request->get('tariff'); @@ -40,7 +40,6 @@ public function download( $status, $fromDate, $toDate, - null ); $this->transactionExportService->createSpreadSheetFromTemplate($this->transactionExportService->getTemplatePath()); $this->transactionExportService->setCurrency($currency); @@ -48,9 +47,8 @@ public function download( $this->transactionExportService->setTransactionData($data); $this->transactionExportService->setExportingData(); $this->transactionExportService->writeTransactionData(); - $this->transactionExportService->saveSpreadSheet($this->path); + $path = $this->transactionExportService->saveSpreadSheet(); - return response()->download($this->path . '/' . - $this->transactionExportService->getRecentlyCreatedSpreadSheetId() . '.xlsx'); + return response()->download($path, 'transactions' . $fromDate . '-' . $toDate . '.xlsx'); } } diff --git a/Website/htdocs/mpmanager/app/Models/AssetRate.php b/Website/htdocs/mpmanager/app/Models/AssetRate.php index 1de832125..6e80832c9 100644 --- a/Website/htdocs/mpmanager/app/Models/AssetRate.php +++ b/Website/htdocs/mpmanager/app/Models/AssetRate.php @@ -17,6 +17,7 @@ * @property int $rate_cost * @property int $remaining * @property string $due_date + * @property AssetPerson assetPerson */ class AssetRate extends BaseModel { diff --git a/Website/htdocs/mpmanager/app/Services/AbstractExportService.php b/Website/htdocs/mpmanager/app/Services/AbstractExportService.php index 52ee7cbcc..b77ad278d 100644 --- a/Website/htdocs/mpmanager/app/Services/AbstractExportService.php +++ b/Website/htdocs/mpmanager/app/Services/AbstractExportService.php @@ -5,28 +5,32 @@ use App\Exceptions\ActiveSheetNotCreatedException; use App\Exceptions\SpreadSheetNotCreatedException; use App\Exceptions\SpreadSheetNotSavedException; +use Carbon\Carbon; +use Illuminate\Support\Collection; use PhpOffice\PhpSpreadsheet\IOFactory; use PhpOffice\PhpSpreadsheet\Reader\IReader; use PhpOffice\PhpSpreadsheet\Spreadsheet; use Illuminate\Support\Facades\Log; +use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; use Webpatser\Uuid\Uuid; -use DateTime; use DateTimeZone; abstract class AbstractExportService { protected IReader $reader; - protected $worksheet; - protected $spreadsheet; - protected $exportingData; - protected $currency; - protected $timeZone; - protected $recentlyCreatedSpreadSheetId; + protected Worksheet $worksheet; + protected Spreadsheet $spreadsheet; + protected Collection $exportingData; + protected string $currency; + protected string $timeZone; + protected string $recentlyCreatedSpreadSheetId; abstract public function setExportingData(); abstract public function getTemplatePath(); + abstract public function getPrefix(); + public function createSpreadSheetFromTemplate(string $path): Spreadsheet { try { @@ -79,26 +83,24 @@ public function readable($amount, $separator = ',') return $decimal ? "$whole.$decimal" : $whole; } - public function convertUtcDateToTimezone($utcDate) + public function convertUtcDateToTimezone($utcDate): string { // Create a DateTime object with the UTC-based date - $dateTimeUtc = new DateTime($utcDate, new DateTimeZone('UTC')); + $dateTimeUtc = Carbon::parse($utcDate)->setTimezone('UTC'); // Set the desired timezone $dateTimeUtc->setTimezone(new DateTimeZone($this->timeZone)); // Format the date and time as a string - $formattedDateTime = $dateTimeUtc->format('Y-m-d H:i:s'); - - return $formattedDateTime; + return $dateTimeUtc->format('Y-m-d H:i:s'); } - public function setRecentlyCreatedSpreadSheetId($id) + public function setRecentlyCreatedSpreadSheetId(string $id): void { $this->recentlyCreatedSpreadSheetId = $id; } - public function setActivatedSheet($sheetName) + public function setActivatedSheet($sheetName): void { try { $this->worksheet = $this->spreadsheet->setActiveSheetIndexByName($sheetName); @@ -110,20 +112,18 @@ public function setActivatedSheet($sheetName) } } - public function saveSpreadSheet($path) + public function saveSpreadSheet(): string { + try { $uuid = (string)Uuid::generate(4); + $fileName = storage_path('appliance') . "/" . $this->getPrefix() . '-' . $uuid . ".xlsx"; $this->setRecentlyCreatedSpreadSheetId($uuid); $writer = IOFactory::createWriter($this->spreadsheet, "Xlsx"); - $writer->save($path . "/" . $uuid . ".xlsx"); + $writer->save($fileName); + return $fileName; } catch (\Exception $e) { throw new SpreadSheetNotSavedException($e->getMessage()); } } - - public function getRecentlyCreatedSpreadSheetId() - { - return $this->recentlyCreatedSpreadSheetId; - } } diff --git a/Website/htdocs/mpmanager/app/Services/ApplianceRateService.php b/Website/htdocs/mpmanager/app/Services/ApplianceRateService.php index 82b8de287..0536e70a7 100644 --- a/Website/htdocs/mpmanager/app/Services/ApplianceRateService.php +++ b/Website/htdocs/mpmanager/app/Services/ApplianceRateService.php @@ -4,22 +4,27 @@ use App\Models\AssetRate; use App\Models\MainSettings; -use App\Models\PaymentHistory; use Carbon\Carbon; +use Carbon\CarbonImmutable; +use Illuminate\Database\Eloquent\Builder; +use Illuminate\Database\Eloquent\Collection; class ApplianceRateService implements IBaseService { - public function __construct(private AssetRate $applianceRate, private MainSettings $mainSettings) - { + public function __construct( + private AssetRate $applianceRate, + private MainSettings $mainSettings + ) { } - public function getCurrencyFromMainSettings() + public function getCurrencyFromMainSettings(): string { + /** @var MainSettings $mainSettings */ $mainSettings = $this->mainSettings->newQuery()->first(); return $mainSettings === null ? '€' : $mainSettings->currency; } - public function updateApplianceRateCost($applianceRate, $creatorId, $cost, $newCost) + public function updateApplianceRateCost(AssetRate $applianceRate, $creatorId, $cost, $newCost): AssetRate { $currency = $this->getCurrencyFromMainSettings(); event( @@ -40,10 +45,10 @@ public function updateApplianceRateCost($applianceRate, $creatorId, $cost, $newC $applianceRate->remaining = $newCost; $applianceRate->update(); $applianceRate->save(); - return $applianceRate->fresh(); + return $applianceRate->refresh(); } - public function deleteUpdatedApplianceRateIfCostZero($applianceRate, $creatorId, $cost, $newCost) + public function deleteUpdatedApplianceRateIfCostZero(AssetRate $applianceRate, $creatorId, $cost, $newCost): void { $currency = $this->getCurrencyFromMainSettings(); $appliancePerson = $applianceRate->assetPerson; @@ -64,7 +69,7 @@ public function deleteUpdatedApplianceRateIfCostZero($applianceRate, $creatorId, ); } - public function getByLoanIdsForDueDate($loanIds) + public function getByLoanIdsForDueDate($loanIds): Collection { return $this->applianceRate->newQuery()->with('assetPerson.asset') ->whereIn('asset_person_id', $loanIds) @@ -73,7 +78,7 @@ public function getByLoanIdsForDueDate($loanIds) ->get(); } - public function getAllByLoanId($loanId) + public function getAllByLoanId($loanId): Collection { return $this->applianceRate->newQuery()->with('assetPerson.asset') ->where('asset_person_id', $loanId) @@ -85,7 +90,7 @@ public function getById($id) // TODO: Implement getById() method. } - public function create($assetPerson, $installmentType = 'monthly') + public function create($assetPerson, $installmentType = 'monthly'): void { $baseTime = $assetPerson->first_payment_date ?? date('Y-m-d'); $installment = $installmentType === 'monthly' ? 'month' : 'week'; @@ -141,20 +146,25 @@ public function getAll($limit = null) } - public function getDownPaymentAsAssetRate($assetPerson): AssetRate + public function getDownPaymentAsAssetRate($assetPerson): ?AssetRate { - return $this->applianceRate->newQuery()->where('asset_person_id', $assetPerson->id) - ->where('rate_cost', $assetPerson->down_payment)->where('remaining', 0)->first(); + /** @var ?AssetRate $result */ + $result = $this->applianceRate->newQuery() + ->where('asset_person_id', $assetPerson->id) + ->where('rate_cost', $assetPerson->down_payment) + ->where('remaining', 0) + ->first(); + + return $result; } - public function getOutstandingDebtsByApplianceRates() + public function queryOutstandingDebtsByApplianceRates(CarbonImmutable $toDate): Builder { return $this->applianceRate->newQuery() ->with(['assetPerson.asset', 'assetPerson.person']) - ->where('due_date', '<', now()->toDateString()) + ->where('due_date', '<', $toDate->format('Y-m-d')) ->where('remaining', '>', 0) ->groupBy('asset_person_id') - ->orderBy('id') - ->get(); + ->orderBy('id'); } } diff --git a/Website/htdocs/mpmanager/app/Services/UserService.php b/Website/htdocs/mpmanager/app/Services/UserService.php index fdbeaaa3e..3a643b106 100644 --- a/Website/htdocs/mpmanager/app/Services/UserService.php +++ b/Website/htdocs/mpmanager/app/Services/UserService.php @@ -9,6 +9,7 @@ use Exception; use Illuminate\Contracts\Pagination\LengthAwarePaginator; use Illuminate\Database\Eloquent\Builder; +use Illuminate\Support\Collection; use MPM\User\Events\UserCreatedEvent; class UserService @@ -124,7 +125,7 @@ public function getCompanyId(): int public function getById($id) { - return $this->user->newQuery()->find($id); + return $this->user->newQuery()->find($id); } public function delete($model) @@ -136,4 +137,9 @@ public function getAll($limit = null) { // TODO: Implement getAll() method. } + + public function getUsers(): Collection + { + return $this->user->newQuery()->get(); + } } diff --git a/Website/htdocs/mpmanager/app/modules/OutstandingDebts/Export/OutstandingDebtsExportService.php b/Website/htdocs/mpmanager/app/modules/OutstandingDebts/Export/OutstandingDebtsExportService.php deleted file mode 100644 index 13ad6c6ef..000000000 --- a/Website/htdocs/mpmanager/app/modules/OutstandingDebts/Export/OutstandingDebtsExportService.php +++ /dev/null @@ -1,50 +0,0 @@ -setActivatedSheet('Sheet1'); - - foreach ($this->exportingData as $key => $value) { - $this->worksheet->setCellValue('A' . ($key + 2), $value[0]); - $this->worksheet->setCellValue('B' . ($key + 2), $value[1]); - $this->worksheet->setCellValue('C' . ($key + 2), $value[2]); - $this->worksheet->setCellValue('D' . ($key + 2), $value[3]); - $this->worksheet->setCellValue('E' . ($key + 2), $value[4]); - } - - foreach ($this->worksheet->getColumnIterator() as $column) { - $this->worksheet->getColumnDimension($column->getColumnIndex())->setAutoSize(true); - } - } - - public function setExportingData() - { - $this->exportingData = $this->outstandingDebtsData->map(function ($applianceRate) { - return [ - $applianceRate->assetPerson->person->name . ' ' . $applianceRate->assetPerson->person->surname, - $applianceRate->assetPerson->asset->name, - $applianceRate->assetPerson->device_serial, - $applianceRate->due_date, - $applianceRate->remaining - ]; - }); - } - - public function setOutstandingDebtsData($outstandingDebtsData) - { - $this->outstandingDebtsData = $outstandingDebtsData; - } - - public function getTemplatePath() - { - return $this->path; - } -} diff --git a/Website/htdocs/mpmanager/app/modules/OutstandingDebts/OutstandingDebtsExportService.php b/Website/htdocs/mpmanager/app/modules/OutstandingDebts/OutstandingDebtsExportService.php new file mode 100644 index 000000000..e286ac81f --- /dev/null +++ b/Website/htdocs/mpmanager/app/modules/OutstandingDebts/OutstandingDebtsExportService.php @@ -0,0 +1,98 @@ +setActivatedSheet('Sheet1'); + + foreach ($this->exportingData as $key => $value) { + $this->worksheet->setCellValue('A' . ($key + 2), $value[0]); + $this->worksheet->setCellValue('B' . ($key + 2), $value[1]); + $this->worksheet->setCellValue('C' . ($key + 2), $value[2]); + $this->worksheet->setCellValue('D' . ($key + 2), $value[3]); + $this->worksheet->setCellValue('E' . ($key + 2), $value[4]); + } + + foreach ($this->worksheet->getColumnIterator() as $column) { + $this->worksheet->getColumnDimension($column->getColumnIndex())->setAutoSize(true); + } + } + + public function setExportingData(): void + { + $this->exportingData = $this->outstandingDebtsData->map(function ($applianceRate) { + return [ + $applianceRate->assetPerson->person->name . ' ' . $applianceRate->assetPerson->person->surname, + $applianceRate->assetPerson->asset->name, + $applianceRate->assetPerson->device_serial, + $applianceRate->due_date, + $applianceRate->remaining + ]; + }); + } + + public function setOutstandingDebtsData($outstandingDebtsData): void + { + $this->outstandingDebtsData = $outstandingDebtsData; + } + + public function getTemplatePath(): string + { + return storage_path('appliance/export_outstanding_debts_template.xlsx'); + } + + public function createReport(CarbonImmutable $toDate): string + { + $currency = $this->applianceRateService->getCurrencyFromMainSettings(); + + $data = $this->applianceService->queryOutstandingDebtsByApplianceRates($toDate)->get(); + $this->createSpreadSheetFromTemplate($this->getTemplatePath()); + $this->setCurrency($currency); + $this->setOutstandingDebtsData($data); + $this->setExportingData(); + $this->writeOutstandingDebtsData(); + return $this->saveSpreadSheet(); + } + + public function sendApplianceDebtsAsEmail(): void + { + $reportDate = CarbonImmutable::now()->endOfWeek()->endOfDay(); + $path = $this->createReport($reportDate); + + $this->userService->getUsers() + ->each(function (User $user) use ($path, $reportDate) { + $this->mailHelper->sendPlain( + $user->email, + 'Outstanding debts report - ' . $reportDate->format('d-m-Y'), + 'Please find attached the outstanding debts report. This report is generated on ' . CarbonImmutable::now()->format('d-m-Y') . '.', + $path + ); + }); + } + + public function getPrefix(): string + { + return 'OutstandingDebtsExport'; + } +} diff --git a/Website/htdocs/mpmanager/app/modules/Transaction/Export/TransactionExportService.php b/Website/htdocs/mpmanager/app/modules/Transaction/Export/TransactionExportService.php index 3124f3075..37d88f1fa 100644 --- a/Website/htdocs/mpmanager/app/modules/Transaction/Export/TransactionExportService.php +++ b/Website/htdocs/mpmanager/app/modules/Transaction/Export/TransactionExportService.php @@ -8,7 +8,7 @@ class TransactionExportService extends AbstractExportService { private $path = __DIR__ . "/export_transactions_template.xlsx"; private $transactionData; - public function writeTransactionData() + public function writeTransactionData(): void { $this->setActivatedSheet('Sheet1'); @@ -27,7 +27,7 @@ public function writeTransactionData() } } - public function setExportingData() + public function setExportingData(): void { $this->exportingData = $this->transactionData->map(function ($transaction) { $status = $transaction->originalTransaction->status == 1 ? 'Success' : ($transaction->status == 0 ? 'Pending' : 'Failed'); @@ -44,13 +44,18 @@ public function setExportingData() }); } - public function setTransactionData($transactionData) + public function setTransactionData($transactionData): void { $this->transactionData = $transactionData; } - public function getTemplatePath() + public function getTemplatePath(): string { - return $this->path; + return storage_path('transaction/export_transactions_template.xlsx'); + } + + public function getPrefix(): string + { + return 'TransactionExport'; } } diff --git a/Website/htdocs/mpmanager/app/modules/OutstandingDebts/Export/export_outstanding_debts_template.xlsx b/Website/htdocs/mpmanager/storage/appliance/export_outstanding_debts_template.xlsx similarity index 100% rename from Website/htdocs/mpmanager/app/modules/OutstandingDebts/Export/export_outstanding_debts_template.xlsx rename to Website/htdocs/mpmanager/storage/appliance/export_outstanding_debts_template.xlsx diff --git a/Website/htdocs/mpmanager/app/modules/Transaction/Export/export_transactions_template.xlsx b/Website/htdocs/mpmanager/storage/transaction/export_transactions_template.xlsx similarity index 100% rename from Website/htdocs/mpmanager/app/modules/Transaction/Export/export_transactions_template.xlsx rename to Website/htdocs/mpmanager/storage/transaction/export_transactions_template.xlsx