- Created ExampleTest and RoutesTest for basic application response and route testing. - Implemented IsolatedDatabaseTestCase for isolated database testing. - Added unit tests for various models and services including Contract, CPE, and LeadDetail. - Developed request validation tests for CreateClientRequest, UpdateClientRequest, and ContractRequest. - Introduced Vite configuration for asset management in the Laravel application.
736 lines
27 KiB
PHP
736 lines
27 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use App\Exports\ContractsExport;
|
|
use App\Http\Requests\ContractRequest;
|
|
use App\Http\Requests\RenewContractRequest;
|
|
use App\Models\{
|
|
ClientType, Contract, ContractHistory, RenewalType
|
|
};
|
|
use App\Traits\ResourcesCache;
|
|
use Carbon\Carbon;
|
|
use Illuminate\Database\Eloquent\Collection;
|
|
use Illuminate\Database\Eloquent\Model;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Maatwebsite\Excel\Facades\Excel;
|
|
|
|
class ContractService
|
|
{
|
|
|
|
use ResourcesCache;
|
|
|
|
/**
|
|
* Displays a paginated list of contracts with applied filters.
|
|
*
|
|
* @param Request $request The HTTP request object containing filter and pagination data.
|
|
*
|
|
* @return \Illuminate\View\View
|
|
*
|
|
* @author Luis Medon <luismedon@gmail.com>
|
|
* @version 2025/06
|
|
* @since 2025/06
|
|
*/
|
|
public function index(Request $request)
|
|
{
|
|
$perPage = max(1, min(100, (int) $request->get('per_page', 10)));
|
|
|
|
$query = Contract::query();
|
|
$this->_applyFilters($query, $request);
|
|
|
|
$filteredQuery = clone $query;
|
|
$contracts = $query->paginate($perPage)->appends($request->query());
|
|
|
|
# Values for the full filtered set, not only the current page
|
|
$totalPvp = (float) $filteredQuery->sum('contract_value');
|
|
$totalCommission = (float) $filteredQuery->sum('commission_value');
|
|
$totalChargeback = (float) $filteredQuery->sum('chargeback_value');
|
|
$totalMargin = $totalPvp - $totalCommission + $totalChargeback;
|
|
|
|
return [
|
|
'contracts' => $contracts,
|
|
'statuses' => $this->_getContractStatuses(),
|
|
'contractTypes' => $this->_getContractTypes(),
|
|
'sellers' => $this->_getSellers(),
|
|
'totalPvp' => $totalPvp,
|
|
'totalCommission' => $totalCommission,
|
|
'totalChargeback' => $totalChargeback,
|
|
'totalMargin' => $totalMargin,
|
|
'suppliers' => $this->_getSuppliers(),
|
|
'clientTypes' => ClientType::orderBy('description')->get(),
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Applies filter parameters to the contract query.
|
|
*
|
|
* @param \Illuminate\Database\Eloquent\Builder $query
|
|
* @param Request $request
|
|
*
|
|
* @return void
|
|
*
|
|
* @author Luis Medon
|
|
*/
|
|
private function _applyFilters($query, Request $request)
|
|
{
|
|
$filters = [
|
|
'contract_id' => fn($q, $v) => $q->where('id', $v),
|
|
'contract_code' => fn($q, $v) => $q->where('code', 'like', "%$v%"),
|
|
'status' => fn($q, $v) => $q->where('status_id', $v),
|
|
'contract_type' => fn($q, $v) => $q->where('contract_type_id', $v),
|
|
'client_type_id' => fn($q, $v) => $q->whereHas(
|
|
'client',
|
|
fn($clientQuery) => $clientQuery->where('client_type_id', $v)
|
|
),
|
|
'supply_type' => fn($q, $v) => $q->where('supply_type', $v),
|
|
'validity_from' => fn($q, $v) => $q->whereDate('contract_date', '>=', $this->normalizeDate($v)),
|
|
'validity_to' => fn($q, $v) => $q->whereDate('contract_date', '<=', $this->normalizeDate($v)),
|
|
'activation_from' => fn($q, $v) => $q->whereDate('activation_date', '>=', $this->normalizeDate($v)),
|
|
'activation_to' => fn($q, $v) => $q->whereDate('activation_date', '<=', $this->normalizeDate($v)),
|
|
'renewal_from' => fn($q, $v) => $q->whereDate('validity', '>=', $this->normalizeDate($v)),
|
|
'renewal_to' => fn($q, $v) => $q->whereDate('validity', '<=', $this->normalizeDate($v)),
|
|
'seller_id' => fn($q, $v) => $q->where('seller_id', $v),
|
|
'supplier_id' => fn($q, $v) => $q->where('new_supplier_id', $v),
|
|
];
|
|
|
|
foreach ($filters as $key => $callback) {
|
|
if ($request->filled($key)) {
|
|
$callback($query, $request->input($key));
|
|
}
|
|
}
|
|
|
|
if ($request->filled('client_name')) {
|
|
$query->whereHas('client', fn($q) => $q->where('name', 'like', '%' . $request->input('client_name') . '%'));
|
|
}
|
|
|
|
if ($request->filled('sort_by')) {
|
|
$allowedSortColumns = [
|
|
'id', 'code', 'status_id', 'contract_type_id', 'supply_type',
|
|
'validity', 'activation_date', 'renewal_date', 'seller_id'
|
|
];
|
|
$sortBy = $request->input('sort_by');
|
|
if (in_array($sortBy, $allowedSortColumns)) {
|
|
$order = strtolower($request->input('order', 'desc'));
|
|
$order = in_array($order, ['asc', 'desc']) ? $order : 'desc';
|
|
$query->orderBy($sortBy, $order);
|
|
} else {
|
|
// Fallback to default sorting if sort_by is invalid
|
|
$query->orderBy('activation_date', 'desc');
|
|
}
|
|
} else {
|
|
// Always apply default sorting when sort_by is not provided
|
|
// Last added contracts first
|
|
$query->orderBy('id', 'desc');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Exports filtered contracts to Excel.
|
|
*
|
|
* @param Request $request
|
|
*
|
|
* @return \Symfony\Component\HttpFoundation\BinaryFileResponse
|
|
*
|
|
* @author Luis Medon
|
|
*/
|
|
public function export(Request $request)
|
|
{
|
|
$filename = 'contratos_' . now()->format('Ymd_His') . '.xlsx';
|
|
return Excel::download(new ContractsExport($request), $filename);
|
|
}
|
|
|
|
/**
|
|
* Shows the contract creation form.
|
|
*
|
|
* @param Request $request
|
|
*
|
|
* @return \Illuminate\View\View
|
|
*
|
|
* @author Luis Medon
|
|
*/
|
|
public function create(Request $request)
|
|
{
|
|
return [
|
|
'clientId' => $request->query('client_id'),
|
|
'contract' => null,
|
|
'paymentMethods' => $this->_getPaymentMethods(),
|
|
'contractTypes' => $this->_getContractTypes(),
|
|
'contractStatus' => $this->_getContractStatuses(),
|
|
'powerTiers' => $this->_getPowerTiers(),
|
|
'cui_tiers' => $this->_getCuiTiers(),
|
|
'suppliers' => $this->_getSuppliers(),
|
|
'typology' => $this->_getTypologies(),
|
|
'sellers' => $this->_getSellers(),
|
|
'installments_count' => 12,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Stores a newly created contract.
|
|
*
|
|
* @param ContractRequest $request
|
|
*
|
|
* @return \Illuminate\Http\RedirectResponse
|
|
*
|
|
* @author Luis Medon
|
|
*/
|
|
public function store(ContractRequest $request)
|
|
{
|
|
$data = $request->validated();
|
|
|
|
if (! $request->user()?->can('view_contract_summary')) {
|
|
foreach (['contract_value', 'commission_value', 'profit_value', 'paid', 'chargeback', 'chargeback_value', 'chargeback_date'] as $field) {
|
|
unset($data[$field]);
|
|
}
|
|
$installmentsInput = [];
|
|
} else {
|
|
$installmentsInput = $request->input('installments', []);
|
|
}
|
|
|
|
foreach (['contract_date', 'activation_date', 'validity', 'contract_end_date', 'renewal_date', 'payment_date', 'chargeback_date'] as $field) {
|
|
if (!empty($data[$field])) {
|
|
$data[$field] = $this->normalizeDate($data[$field]);
|
|
}
|
|
}
|
|
|
|
if (($data['delivery_method'] ?? null) !== 'email') {
|
|
$data['delivery_email'] = null;
|
|
}
|
|
|
|
if (empty($data['chargeback'])) {
|
|
$data['chargeback_value'] = null;
|
|
$data['chargeback_date'] = null;
|
|
}
|
|
|
|
if ($request->user()?->can('view_contract_summary')) {
|
|
$data['profit_value'] = (float) ($data['contract_value'] ?? 0)
|
|
- abs((float) ($data['commission_value'] ?? 0))
|
|
+ (float) ($data['chargeback_value'] ?? 0);
|
|
}
|
|
|
|
$contract = Contract::create($data);
|
|
|
|
foreach ($installmentsInput as $i => $installment) {
|
|
if (!empty($installment['due_date']) && !empty($installment['amount'])) {
|
|
$dueDate = $this->normalizeDate($installment['due_date']);
|
|
if ($dueDate === null) {
|
|
continue;
|
|
}
|
|
|
|
$contract->installments()->create([
|
|
'installment_number' => $i,
|
|
'due_date' => $dueDate,
|
|
'amount' => $installment['amount'],
|
|
'paid' => isset($installment['paid']) && $installment['paid'] ? 1 : 0,
|
|
]);
|
|
}
|
|
}
|
|
|
|
return $contract;
|
|
}
|
|
|
|
|
|
/**
|
|
* Shows the contract editing form.
|
|
*
|
|
* @param string $id
|
|
*
|
|
* @return \Illuminate\View\View
|
|
*
|
|
* @throws \Illuminate\Database\Eloquent\ModelNotFoundException
|
|
*
|
|
* @author Luis Medon
|
|
*/
|
|
public function edit(string $id)
|
|
{
|
|
$contract = Contract::findOrFail($id);
|
|
|
|
$suppliers = $this->includeCurrentOption($this->_getSuppliers(), $contract->previousSupplier);
|
|
$suppliers = $this->includeCurrentOption($suppliers, $contract->newSupplier);
|
|
|
|
return [
|
|
'contract' => $contract,
|
|
'clientId' => $contract->client_id,
|
|
'paymentMethods' => $this->includeCurrentOption($this->_getPaymentMethods(), $contract->paymentMethod),
|
|
'contractTypes' => $this->includeCurrentOption($this->_getContractTypes(), $contract->contractType),
|
|
'contractStatus' => $this->_getContractStatuses(),
|
|
'powerTiers' => $this->includeCurrentOption($this->_getPowerTiers(), $contract->powerTier),
|
|
'cui_tiers' => $this->includeCurrentOption($this->_getCuiTiers(), $contract->cuiTier),
|
|
'suppliers' => $suppliers,
|
|
'typology' => $this->includeCurrentOption($this->_getTypologies(), $contract->typology),
|
|
'sellers' => $this->includeCurrentOption($this->_getSellers(), $contract->seller),
|
|
'installments_count' => 12,
|
|
];
|
|
}
|
|
|
|
private function includeCurrentOption(Collection $options, ?Model $current): Collection
|
|
{
|
|
$options = clone $options;
|
|
|
|
if ($current && ! $options->contains(fn (Model $option) => $option->is($current))) {
|
|
$options->prepend($current);
|
|
}
|
|
|
|
return $options;
|
|
}
|
|
|
|
/**
|
|
* Updates an existing contract.
|
|
*
|
|
* @param ContractRequest $request
|
|
* @param string $id
|
|
*
|
|
* @return \Illuminate\Http\RedirectResponse
|
|
*
|
|
* @throws \Illuminate\Database\Eloquent\ModelNotFoundException
|
|
*
|
|
* @author Luis Medon <luismedon@gmail.com>
|
|
*/
|
|
public function update(ContractRequest $request, string $id)
|
|
{
|
|
$contract = Contract::findOrFail($id);
|
|
$data = $request->validated();
|
|
|
|
if (! $request->user()?->can('view_contract_summary')) {
|
|
foreach (['contract_value', 'commission_value', 'profit_value', 'paid', 'chargeback', 'chargeback_value', 'chargeback_date'] as $field) {
|
|
unset($data[$field]);
|
|
}
|
|
$installmentsInput = [];
|
|
} else {
|
|
$installmentsInput = $request->input('installments', []);
|
|
}
|
|
|
|
// Campos de data do contrato
|
|
foreach (['contract_date', 'activation_date', 'validity', 'contract_end_date', 'renewal_date', 'payment_date', 'chargeback_date'] as $field) {
|
|
if (!empty($data[$field])) {
|
|
$data[$field] = $this->normalizeDate($data[$field]);
|
|
}
|
|
}
|
|
|
|
if (($data['delivery_method'] ?? null) !== 'email') {
|
|
$data['delivery_email'] = null;
|
|
}
|
|
|
|
if ($request->user()?->can('view_contract_summary') && empty($data['chargeback'])) {
|
|
$data['chargeback_value'] = null;
|
|
$data['chargeback_date'] = null;
|
|
}
|
|
|
|
if ($request->user()?->can('view_contract_summary')) {
|
|
$data['profit_value'] = (float) ($data['contract_value'] ?? 0)
|
|
- abs((float) ($data['commission_value'] ?? 0))
|
|
+ (float) ($data['chargeback_value'] ?? 0);
|
|
}
|
|
|
|
if (! $contract->update($data)) {
|
|
throw \Illuminate\Validation\ValidationException::withMessages([
|
|
'contract' => 'O contrato não foi atualizado. Reveja os dados e tente novamente.',
|
|
]);
|
|
}
|
|
|
|
// Parcelas
|
|
$processedIds = [];
|
|
|
|
foreach ($installmentsInput as $i => $installment) {
|
|
$dueDateRaw = trim($installment['due_date'] ?? '');
|
|
$amount = $installment['amount'] ?? null;
|
|
|
|
if ($dueDateRaw !== '' && $amount !== null) {
|
|
$dueDate = $this->normalizeDate($dueDateRaw);
|
|
if ($dueDate === null) {
|
|
continue;
|
|
}
|
|
|
|
$payload = [
|
|
'installment_number' => $i,
|
|
'due_date' => $dueDate,
|
|
'amount' => $amount,
|
|
'paid' => isset($installment['paid']) && $installment['paid'] ? 1 : 0,
|
|
];
|
|
|
|
$existing = $contract->installments()->where('installment_number', $i)->first();
|
|
|
|
if ($existing) {
|
|
$existing->update($payload);
|
|
$processedIds[] = $existing->id;
|
|
} else {
|
|
$new = $contract->installments()->create($payload);
|
|
$processedIds[] = $new->id;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Deleta parcelas não enviadas
|
|
if ($request->user()?->can('view_contract_summary')) {
|
|
$contract->installments()->whereNotIn('id', $processedIds)->delete();
|
|
}
|
|
|
|
return $contract;
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
* Shows contract details including financial summary.
|
|
*
|
|
* @param string $id
|
|
*
|
|
* @return \Illuminate\View\View
|
|
*
|
|
* @throws \Illuminate\Database\Eloquent\ModelNotFoundException
|
|
*
|
|
* @author Luis Medon
|
|
*/
|
|
public function show(string $id)
|
|
{
|
|
$contract = Contract::with('payments')->findOrFail($id);
|
|
$lastRef = $contract->payments()->orderByDesc('reference_period')->value('reference_period');
|
|
|
|
$totalReceived = $totalPending = $commission = $profit = 0;
|
|
|
|
if ($lastRef) {
|
|
$payments = $contract->payments()->whereDate('reference_period', $lastRef)->get();
|
|
$totalReceived = $payments->where('status', 'paid')->sum('amount');
|
|
$totalPending = $payments->where('status', 'pending')->sum('amount');
|
|
$commission = $contract->pvp * ($contract->commission_rate / 100);
|
|
$profit = ($contract->pvp - $commission) - $contract->net
|
|
+ (float) ($contract->chargeback_value ?? 0);
|
|
}
|
|
|
|
return [
|
|
'contract' => $contract,
|
|
'contractTypes' => $this->_getContractTypes(),
|
|
'statuses' => $this->_getContractStatuses(),
|
|
'totalReceived' => $totalReceived,
|
|
'totalPending' => $totalPending,
|
|
'commission' => $commission,
|
|
'profit' => $profit,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Displays the contract renewal form.
|
|
*
|
|
* @param string $id
|
|
*
|
|
* @return \Illuminate\View\View
|
|
*
|
|
* @throws \Illuminate\Database\Eloquent\ModelNotFoundException
|
|
*
|
|
* @author Luis Medon
|
|
*/
|
|
public function renewForm(string $id)
|
|
{
|
|
$contract = Contract::findOrFail($id);
|
|
return [
|
|
'contract' => $contract,
|
|
'renewalTypes' => RenewalType::orderBy('id')->get(),
|
|
'suppliers' => $this->_getSuppliers(),
|
|
'contractTypes' => $this->_getContractTypes(),
|
|
'typology' => $this->_getTypologies(),
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Performs contract renewal and creates payment.
|
|
*
|
|
* @param string $id
|
|
*
|
|
* @return \Illuminate\Http\RedirectResponse
|
|
*
|
|
* @throws \Illuminate\Database\Eloquent\ModelNotFoundException
|
|
*
|
|
* @author Luis Medon <luismedon@gmail.com>
|
|
*/
|
|
public function renew(RenewContractRequest $request, $id)
|
|
{
|
|
# Obtain contract information
|
|
$contract = Contract::findOrFail($id);
|
|
|
|
# Obtain contract installemnts
|
|
$installments = $contract->installments()->get();
|
|
$totalInstallments = $installments->count();
|
|
$totalPaidInstallments = $installments->where('paid', true)->count();
|
|
$canViewFinancials = $request->user()?->can('view_contract_summary') ?? false;
|
|
$currentSupplierId = $request->boolean('change_supplier')
|
|
? $request->new_supplier_id
|
|
: $contract->new_supplier_id;
|
|
|
|
# Create Contract History Record
|
|
$historyData = [
|
|
'contract_id' => $contract->id,
|
|
'previous_supplier_id' => $contract->new_supplier_id,
|
|
'current_supplier_id' => $currentSupplierId,
|
|
'seller_id' => $contract->seller_id,
|
|
'contract_type_id' => $request->contract_type_id,
|
|
'typology_id' => $request->typology_id,
|
|
'contract_date' => $contract->contract_date,
|
|
'activation_date' => $contract->activation_date,
|
|
'renewal_date' => $contract->validity,
|
|
'renewal_type' => $request->renewal_type,
|
|
'go_ki' => $contract->go_ki,
|
|
'validity' => $contract->validity,
|
|
'contract_value' => $contract->contract_value,
|
|
'commission_value' => $contract->commission_value,
|
|
'profit_value' => $contract->profit_value,
|
|
'annual_consumption' => $contract->annual_consumption,
|
|
'total_installments' => $totalInstallments,
|
|
'paid_installments' => $totalPaidInstallments,
|
|
'notes' => 'Renovação automática',
|
|
'contract_snapshot' => $contract->toJson(),
|
|
];
|
|
|
|
if ($canViewFinancials) {
|
|
$historyData['chargeback'] = (bool) $contract->chargeback;
|
|
$historyData['chargeback_value'] = $contract->chargeback_value;
|
|
$historyData['chargeback_date'] = $contract->chargeback_date;
|
|
}
|
|
|
|
ContractHistory::create($historyData);
|
|
|
|
# Update current contract data
|
|
$contractData = [
|
|
'annual_consumption' => $request->annual_consumption,
|
|
'contract_date' => $request->contract_date,
|
|
'activation_date' => $request->activation_date,
|
|
'contract_end_date' => $request->contract_end_date,
|
|
'validity' => $request->next_due_date,
|
|
'contract_type_id' => $request->contract_type_id,
|
|
'typology_id' => $request->typology_id,
|
|
'go_ki' => $request->go_ki,
|
|
'paid' => true,
|
|
'previous_supplier_id' => $contract->new_supplier_id,
|
|
];
|
|
|
|
if ($canViewFinancials) {
|
|
$contractData['contract_value'] = $request->amount_paid;
|
|
$contractData['commission_value'] = $request->commission_value;
|
|
$contractData['profit_value'] = (float) $request->amount_paid - abs((float) $request->commission_value) + (float) ($contract->chargeback_value ?? 0);
|
|
}
|
|
|
|
if ($request->boolean('change_supplier')) {
|
|
$contractData['new_supplier_id'] = $request->new_supplier_id;
|
|
}
|
|
|
|
$contract->update($contractData);
|
|
|
|
return $contract;
|
|
}
|
|
|
|
|
|
public function history($id)
|
|
{
|
|
$contract = Contract::findOrFail($id);
|
|
|
|
$histories = ContractHistory::with(['previousSupplier', 'currentSupplier', 'seller', 'contractType'])
|
|
->where('contract_id', $contract->id)
|
|
->orderByRaw('COALESCE(validity, contract_date, created_at) ASC')
|
|
->paginate(10);
|
|
|
|
$totals = ContractHistory::where('contract_id', $contract->id)
|
|
->selectRaw('COUNT(*) as renewals_count')
|
|
->selectRaw('COALESCE(SUM(total_installments), 0) as total_installments')
|
|
->selectRaw('COALESCE(SUM(paid_installments), 0) as paid_installments')
|
|
->selectRaw('COALESCE(SUM(contract_value), 0) as contract_value')
|
|
->selectRaw('COALESCE(SUM(commission_value), 0) as commission_value')
|
|
->selectRaw('COALESCE(SUM(chargeback_value), 0) as chargeback_value')
|
|
->selectRaw('COALESCE(SUM(profit_value), 0) as profit_value')
|
|
->first();
|
|
|
|
return compact('histories', 'totals', 'contract');
|
|
}
|
|
|
|
public function createHistory($id)
|
|
{
|
|
$contract = Contract::findOrFail($id);
|
|
|
|
return [
|
|
'contract' => $contract,
|
|
'history' => null,
|
|
'suppliers' => $this->_getSuppliers(),
|
|
];
|
|
}
|
|
|
|
public function storeHistory(Request $request, $id)
|
|
{
|
|
$contract = Contract::findOrFail($id);
|
|
|
|
$rules = [
|
|
'previous_supplier_id' => ['nullable', 'exists:suppliers,id'],
|
|
'current_supplier_id' => ['nullable', 'exists:suppliers,id'],
|
|
'validity' => ['nullable', 'date'],
|
|
'annual_consumption' => ['nullable', 'numeric'],
|
|
'seller_id' => ['nullable', 'exists:users,id'],
|
|
'contract_type_id' => ['nullable', 'exists:contract_types,id'],
|
|
'typology_id' => ['nullable', 'exists:typologies,id'],
|
|
'contract_date' => ['nullable', 'date'],
|
|
'activation_date' => ['nullable', 'date'],
|
|
'renewal_date' => ['nullable', 'date'],
|
|
'renewal_type' => ['nullable', 'string'],
|
|
'go_ki' => ['nullable', 'numeric'],
|
|
'contract_value' => ['nullable', 'numeric'],
|
|
'commission_value' => ['nullable', 'numeric'],
|
|
'profit_value' => ['nullable', 'numeric'],
|
|
'chargeback' => ['nullable', 'boolean'],
|
|
'chargeback_value' => ['nullable', 'numeric', 'max:-0.01'],
|
|
'chargeback_date' => ['nullable', 'date'],
|
|
'notes' => ['nullable', 'string'],
|
|
];
|
|
|
|
if (! $request->user()?->can('view_contract_summary')) {
|
|
foreach (['contract_value', 'commission_value', 'profit_value', 'chargeback', 'chargeback_value', 'chargeback_date'] as $field) {
|
|
unset($rules[$field]);
|
|
}
|
|
}
|
|
|
|
$data = $request->validate($rules);
|
|
|
|
foreach (['validity', 'contract_date', 'activation_date', 'renewal_date', 'chargeback_date'] as $field) {
|
|
if (! empty($data[$field])) {
|
|
$data[$field] = $this->normalizeDate($data[$field]);
|
|
}
|
|
}
|
|
|
|
if (empty($data['chargeback'] ?? null)) {
|
|
$data['chargeback'] = false;
|
|
$data['chargeback_value'] = null;
|
|
$data['chargeback_date'] = null;
|
|
}
|
|
|
|
$data['contract_id'] = $contract->id;
|
|
|
|
if ($request->user()?->can('view_contract_summary') && ! empty($data['contract_value']) && ! empty($data['commission_value'])) {
|
|
$data['profit_value'] = (float) ($data['contract_value'] ?? 0)
|
|
- abs((float) ($data['commission_value'] ?? 0))
|
|
+ (float) ($data['chargeback_value'] ?? 0);
|
|
}
|
|
|
|
return ContractHistory::create($data);
|
|
}
|
|
|
|
public function editHistory($id, $historyId)
|
|
{
|
|
$contract = Contract::findOrFail($id);
|
|
$history = ContractHistory::where('contract_id', $contract->id)->findOrFail($historyId);
|
|
|
|
return [
|
|
'contract' => $contract,
|
|
'history' => $history,
|
|
'suppliers' => $this->_getSuppliers(),
|
|
];
|
|
}
|
|
|
|
private function normalizeDate($value): ?string
|
|
{
|
|
if ($value === null || $value === '') {
|
|
return null;
|
|
}
|
|
|
|
$value = trim((string) $value);
|
|
|
|
try {
|
|
if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $value)) {
|
|
return Carbon::createFromFormat('Y-m-d', $value)->format('Y-m-d');
|
|
}
|
|
|
|
if (preg_match('/^\d{2}\/\d{2}\/\d{4}$/', $value)) {
|
|
return Carbon::createFromFormat('d/m/Y', $value)->format('Y-m-d');
|
|
}
|
|
|
|
return Carbon::parse($value)->format('Y-m-d');
|
|
} catch (\Exception $e) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public function updateHistory(Request $request, $id, $historyId)
|
|
{
|
|
$contract = Contract::findOrFail($id);
|
|
$history = ContractHistory::where('contract_id', $contract->id)->findOrFail($historyId);
|
|
|
|
$data = $request->validate($this->getHistoryUpdateRules($request));
|
|
|
|
if (! $request->user()?->can('view_contract_summary')) {
|
|
foreach (['contract_value', 'commission_value', 'profit_value', 'chargeback', 'chargeback_value', 'chargeback_date'] as $field) {
|
|
unset($data[$field]);
|
|
}
|
|
}
|
|
|
|
$history->update($data);
|
|
|
|
return $history;
|
|
}
|
|
|
|
protected function getHistoryUpdateRules(Request $request): array
|
|
{
|
|
$rules = [
|
|
'previous_supplier_id' => ['nullable', 'exists:suppliers,id'],
|
|
'validity' => ['nullable', 'date'],
|
|
'annual_consumption' => ['nullable', 'numeric'],
|
|
'notes' => ['nullable', 'string'],
|
|
];
|
|
|
|
if ($request->user()?->can('view_contract_summary')) {
|
|
$rules['contract_value'] = ['required', 'numeric'];
|
|
$rules['commission_value'] = ['nullable', 'numeric'];
|
|
$rules['profit_value'] = ['nullable', 'numeric'];
|
|
$rules['chargeback'] = ['nullable', 'boolean'];
|
|
$rules['chargeback_value'] = ['nullable', 'numeric', 'max:-0.01'];
|
|
$rules['chargeback_date'] = ['nullable', 'date'];
|
|
}
|
|
|
|
return $rules;
|
|
}
|
|
|
|
public function destroyHistory($id, $historyId)
|
|
{
|
|
$contract = Contract::findOrFail($id);
|
|
$history = ContractHistory::where('contract_id', $contract->id)->findOrFail($historyId);
|
|
$history->delete();
|
|
|
|
return $history;
|
|
}
|
|
|
|
|
|
/**
|
|
* Deletes a contract from the system.
|
|
*
|
|
* @param string $id
|
|
*
|
|
* @return \Illuminate\Http\JsonResponse|\Illuminate\Http\RedirectResponse
|
|
*
|
|
* @throws \Illuminate\Database\Eloquent\ModelNotFoundException
|
|
*
|
|
* @author Luis Medon
|
|
*/
|
|
public function destroy(string $id)
|
|
{
|
|
$contract = Contract::findOrFail($id);
|
|
$contract->delete();
|
|
|
|
return ['success' => true, 'message' => 'Contrato removido com sucesso.', 'icon' => 'success'];
|
|
}
|
|
|
|
public function bulkDestroy(Request $request)
|
|
{
|
|
$data = $request->validate([
|
|
'ids' => ['required', 'array', 'min:1'],
|
|
'ids.*' => ['integer', 'distinct', 'exists:contracts,id'],
|
|
]);
|
|
|
|
DB::transaction(function () use ($data) {
|
|
Contract::whereIn('id', $data['ids'])->get()->each->delete();
|
|
});
|
|
|
|
return [
|
|
'success' => true,
|
|
'message' => count($data['ids']) === 1
|
|
? 'Contrato removido com sucesso.'
|
|
: 'Contratos removidos com sucesso.',
|
|
'icon' => 'success',
|
|
];
|
|
}
|
|
}
|