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 project invoice terms #3672

Merged
merged 65 commits into from
Jul 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
b635be6
project invoice terms
Jun 28, 2024
cd0ed37
project invoice terms
Jun 28, 2024
1e42f6b
project invoice terms
Jun 28, 2024
7e37e89
project invoice terms
Jun 28, 2024
e3e3c5d
project invoice terms
Jun 28, 2024
9df9ec2
synced with main
Jun 28, 2024
853bc30
synced with main
Jun 28, 2024
9cb897f
synced with main
Jun 28, 2024
d1ee044
synced with main
Jun 28, 2024
a962658
synced with main
Jun 28, 2024
f18f09b
synced with main
Jun 28, 2024
a11a171
synced with main
Jun 28, 2024
eeb1c0e
synced with main
Jun 28, 2024
33d6956
synced with main
Jun 28, 2024
7a4330f
synced with main
Jun 28, 2024
adb8aaf
synced with main
Jun 28, 2024
bb3a3d2
synced with main
Jul 1, 2024
8bd9a93
synced with main
Jul 1, 2024
1039f7f
synced with main
Jul 1, 2024
1dceff7
synced with main
Jul 1, 2024
67a08dd
synced with main
Jul 1, 2024
70b7054
synced with main
Jul 2, 2024
c87c2d9
synced with main
Jul 2, 2024
14afb4a
synced with main
Jul 2, 2024
ded6e10
synced with main
Jul 2, 2024
f3d6da9
synced with main
Jul 2, 2024
cbf9cc6
CI checks
Jul 2, 2024
4f72cd0
CI checks
Jul 2, 2024
e530572
CI checks
Jul 2, 2024
01e8740
CI checks
Jul 2, 2024
2359520
CI checks
Jul 2, 2024
5f1711a
CI checks
Jul 2, 2024
e413d0c
CI checks
Jul 2, 2024
5d2f969
CI checks
Jul 2, 2024
45a6ce1
CI checks
Jul 2, 2024
9554090
CI checks
Jul 2, 2024
6be349e
CI checks
Jul 2, 2024
aad1d6d
CI checks
Jul 2, 2024
c5f7876
CI checks
Jul 2, 2024
8285394
CI checks
Jul 2, 2024
1faff04
CI checks
Jul 2, 2024
8778a4e
CI checks
Jul 2, 2024
7743074
CI checks
Jul 2, 2024
c23d68b
CI checks
Jul 3, 2024
c1b1fd4
CI checks
Jul 3, 2024
02ada5a
CI checks
Jul 3, 2024
e3f15ed
CI checks
Jul 3, 2024
35bfac8
CI checks
Jul 3, 2024
86180da
CI checks
Jul 4, 2024
7850b6a
synced with master
Jul 4, 2024
2470c02
CI checks
Jul 4, 2024
ddf5345
CI checks
Jul 4, 2024
e8586b6
CI checks
Jul 4, 2024
d53fb86
CI checks
Jul 4, 2024
30c1c3a
CI checks
Jul 4, 2024
cafb251
CI checks
Jul 4, 2024
dede36a
CI checks
Jul 4, 2024
3aa83c4
CI checks
Jul 4, 2024
04ded8f
CI checks
Jul 4, 2024
f79cac6
CI checks
Jul 4, 2024
8a5dfb9
CI checks
Jul 5, 2024
c350670
CI checks
Jul 5, 2024
079050d
CI checks
Jul 5, 2024
7aa9b1f
CI checks
Jul 5, 2024
9817e1b
CI checks
Jul 5, 2024
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
37 changes: 37 additions & 0 deletions Modules/Invoice/Console/SendUpcomingInvoiceReminder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

namespace Modules\Invoice\Console;

use Illuminate\Console\Command;
use Illuminate\Support\Facades\Mail;
use Modules\Invoice\Emails\SendInvoiceReminderMail;
use Modules\Invoice\Services\InvoiceService;

class SendUpcomingInvoiceReminder extends Command
{
/**
* The console command name.
*
* @var string
*/
protected $name = 'invoice:send-upcoming-invoice-list';

/**
* The console command description.
*
* @var string
*/
protected $description = 'Send a list of all the upcoming invoices to the finance team.';

/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$service = app(InvoiceService::class);
$upcomingInvoices = $service->getScheduledInvoicesForMail();
Mail::send(new SendInvoiceReminderMail($upcomingInvoices));
}
}
39 changes: 39 additions & 0 deletions Modules/Invoice/Emails/SendInvoiceReminderMail.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

namespace Modules\Invoice\Emails;

use Illuminate\Bus\Queueable;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;

class SendInvoiceReminderMail extends Mailable
{
use Queueable, SerializesModels;

protected $upcomingInvoices;

/**
* Create a new message instance.
*
* @return void
*/
public function __construct(Collection $upcomingInvoices)
{
$this->upcomingInvoices = $upcomingInvoices;
}

/**
* Build the message.
*
* @return $this
*/
public function build()
{
return $this->to(config('constants.finance.scheduled-invoice.email'))
->from(config('mail.from.address'))
->subject('Upcoming Scheduled Invoices Notification')
->view('invoice::mail.upcoming-invoice-list')
->with(['upcomingInvoices' => $this->upcomingInvoices]);
}
}
3 changes: 1 addition & 2 deletions Modules/Invoice/Http/Controllers/InvoiceController.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,7 @@ public function index(Request $request)
if ($invoiceStatus == 'sent') {
unset($filters['invoice_status']);
$filters = $filters ?: $this->service->defaultFilters();
} else {
$invoiceStatus = 'ready';
} elseif ($invoiceStatus == 'ready') {
$filters = $request->all();
}

Expand Down
1 change: 1 addition & 0 deletions Modules/Invoice/Providers/InvoiceServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ protected function registerCommands()
$this->commands([
\Modules\Invoice\Console\SendUnpaidInvoiceList::class,
\Modules\Invoice\Console\FixInvoiceAmountsCommand::class,
\Modules\Invoice\Console\SendUpcomingInvoiceReminder::class,
]);
}

Expand Down
708 changes: 383 additions & 325 deletions Modules/Invoice/Resources/views/index.blade.php

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<div>
<style>
.line {
line-height: 1px;
}
.table {
width: 100%;
border-collapse: collapse;
}
.table th, .table td {
border: 1px solid #ddd;
padding: 8px;
}
.table th {
background-color: #f2f2f2;
}
.text-danger {
color: red;
}
.theme-info {
color: blue;
}
.underline {
text-decoration: underline;
}
</style>
<p>Hello Finance Team!</p>
<p>Please find a list of scheduled invoices.</p>
<p>Total upcoming invoices: {{ $upcomingInvoices->count() }}</p>

<div>
<table class="table">
<thead>
<tr>
<th>Project Name</th>
<th>Invoice Date</th>
<th>Delivery Report</th>
</tr>
</thead>
<tbody>
@foreach($upcomingInvoices as $index => $invoice)
<tr>
<td>{{ $invoice->project->name }}</td>
<td>{{ \Carbon\Carbon::parse($invoice->invoice_date)->format('d-m-Y') }}</td>
<td>
@if ($invoice->delivery_report)
<a id="delivery_report_{{ $index }}" href="{{ route('delivery-report.show', $invoice->id) }}" target="_blank">
<span class="mr-1 underline theme-info fz-16">{{ basename($invoice->delivery_report) }}</span>
</a>
@else
<span class="text-danger">Not Uploaded Yet.</span>
@endif
</td>
</tr>
@endforeach
</tbody>
</table>
</div>

<p><a href="{{ route('invoice.index', ['invoice_status' => 'scheduled']) }}">You can see more details here.</a></p>

<p>Please reach out in case you want to make some changes to this email.</p>
</div>
56 changes: 52 additions & 4 deletions Modules/Invoice/Services/InvoiceService.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
use Modules\Invoice\Exports\YearlyInvoiceReportExport;
use Modules\Invoice\Notifications\GoogleChat\SendPaymentReceivedNotification;
use Modules\Project\Entities\Project;
use Modules\Project\Entities\ProjectInvoiceTerm;

class InvoiceService implements InvoiceServiceContract
{
Expand All @@ -37,16 +38,22 @@ public function index($filters = [], $invoiceStatus = 'sent')
'year' => $filters['year'] ?? null,
'status' => $filters['status'] ?? null,
];

$invoices = [];
$clientsReadyToSendInvoicesData = [];
$projectsReadyToSendInvoicesData = [];
$totalReceivableAmount = 0.00;

if ($invoiceStatus == 'sent') {
$invoices = Invoice::query()->with('client', 'client.contactPersons', 'client.billingDetails')->applyFilters($filters)->leftjoin('clients', 'invoices.client_id', '=', 'clients.id')
->select('invoices.*', 'clients.name')
->where('clients.is_billable', true)
->orderBy('name', 'asc')->orderBy('sent_on', 'desc')
->get();
$clientsReadyToSendInvoicesData = [];
$projectsReadyToSendInvoicesData = [];
$totalReceivableAmount = $this->getTotalReceivableAmountInINR($invoices);
} elseif ($invoiceStatus == 'scheduled') {
$invoices = $this->getScheduledInvoices();
} else {
$invoices = [];
$clientsReadyToSendInvoicesData = Client::status('active')->billable()->invoiceReadyToSend()->orderBy('name')->get();
$projectsReadyToSendInvoicesData = Project::billable()->whereHas('meta', function ($query) {
return $query->where([
Expand All @@ -60,7 +67,7 @@ public function index($filters = [], $invoiceStatus = 'sent')
'invoices' => $invoices,
'clients' => $this->getClientsForInvoice(),
'currencyService' => $this->currencyService(),
'totalReceivableAmount' => $this->getTotalReceivableAmountInINR($invoices),
'totalReceivableAmount' => $totalReceivableAmount,
'filters' => $filters,
'invoiceStatus' => $invoiceStatus,
'clientsReadyToSendInvoicesData' => $clientsReadyToSendInvoicesData,
Expand Down Expand Up @@ -160,6 +167,7 @@ public function store($data)
$invoice = Invoice::create($data);
$this->saveInvoiceFile($invoice, $data['invoice_file']);
$this->setInvoiceNumber($invoice, $data['sent_on']);
$this->updateScheduledInvoice($invoice);

return $invoice;
}
Expand All @@ -185,6 +193,7 @@ public function update($data, $invoice)
$this->saveInvoiceFile($invoice, $data['invoice_file']);
$this->setInvoiceNumber($invoice, $data['sent_on']);
}
$this->updateScheduledInvoice($invoice);

return $invoice;
}
Expand Down Expand Up @@ -769,6 +778,45 @@ public function storeLedgerAccountData(array $data)
LedgerAccount::destroy($ledgerAccountsIdToDelete);
}

public function updateScheduledInvoice($invoice)
{
$project = $invoice->project;
if (! $project) {
return;
}

$invoiceSentOn = Carbon::parse($invoice->sent_on)->toDateString();
$invoiceStatus = $invoice->status;
$invoiceId = $invoice->id;

foreach ($project->invoiceTerms as $scheduledInvoice) {
if (Carbon::parse($scheduledInvoice->invoice_date)->toDateString() === $invoiceSentOn) {
$scheduledInvoice->update([
'status' => $invoiceStatus,
'invoice_id' => $invoiceId,
]);
}
}
}

public function getScheduledInvoices()
{
return ProjectInvoiceTerm::with('project')
->whereNotIn('status', ['sent', 'paid'])
->get();
}

public function getScheduledInvoicesForMail()
{
$currentDate = Carbon::now();

return ProjectInvoiceTerm::where('invoice_date', '<=', Carbon::now()->addDays(config('constants.finance.scheduled-invoice.finance-team-invoice-reminder-days')))
->whereNotIn('status', ['sent', 'paid'])
->whereMonth('invoice_date', strval($currentDate->month))
->with('project')
->get();
}

private function setInvoiceNumber($invoice, $sent_date)
{
$invoice->invoice_number = $this->getInvoiceNumber($invoice->client_id, $invoice->project_id, $sent_date, $invoice->billing_level);
Expand Down
39 changes: 39 additions & 0 deletions Modules/Project/Console/DeliveryReportReminder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

namespace Modules\Project\Console;

use Illuminate\Console\Command;
use Illuminate\Support\Facades\Mail;
use Modules\Project\Emails\DeliveryReportReminderMail;
use Modules\Project\Services\ProjectService;

class DeliveryReportReminder extends Command
{
/**
* The console command name.
*
* @var string
*/
protected $signature = 'project:send-pending-delivery-report-reminder';

/**
* The console command description.
*
* @var string
*/
protected $description = 'Send reminders about pending delivery reports to key account managers.';

/**
* Execute the console command.
*
* @return void
*/
public function handle()
{
$service = app(ProjectService::class);
$keyAccountManagers = $service->getPendingDeliveryReportInvoices();
foreach ($keyAccountManagers as $keyAccountManager) {
Mail::send(new DeliveryReportReminderMail($keyAccountManager));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateProjectInvoiceTerms extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('project_invoice_terms', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('project_id');
$table->unsignedBigInteger('invoice_id')->nullable();
$table->date('invoice_date')->nullable();
$table->decimal('amount', 10, 2)->nullable();
$table->string('status')->nullable();
$table->boolean('client_acceptance_required')->nullable();
$table->boolean('is_accepted')->nullable();
$table->boolean('report_required')->nullable();
$table->string('delivery_report')->nullable();
$table->timestamps();

$table->foreign('project_id')->references('id')->on('projects')->onDelete('cascade');
$table->foreign('invoice_id')->references('id')->on('invoices')->onDelete('cascade');
$table->index('project_id');
$table->index('invoice_id');
});
}

/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('project_invoice_terms');
}
}
37 changes: 37 additions & 0 deletions Modules/Project/Emails/DeliveryReportReminderMail.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

namespace Modules\Project\Emails;

use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;

class DeliveryReportReminderMail extends Mailable
{
use Queueable, SerializesModels;

public $keyAccountManager;

/**
* Create a new message instance.
*
* @return void
*/
public function __construct($keyAccountManager)
{
$this->keyAccountManager = $keyAccountManager;
}

/**
* Build the message.
*
* @return $this
*/
public function build()
{
return $this->subject('Action Required: Missing Delivery Reports for Scheduled Invoices')
->from(config('mail.from.address'))
->to($this->keyAccountManager->email)
->view('project::mail.delivery-report-reminder');
}
}
Loading
Loading