Initial support for importing supplier costs
This commit is contained in:
parent
606f357839
commit
768744ad27
41
app/Console/Commands/ImportCosts.php
Normal file
41
app/Console/Commands/ImportCosts.php
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Commands;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Illuminate\Console\Command;
|
||||||
|
|
||||||
|
use App\Jobs\ImportCosts as Job;
|
||||||
|
use App\Models\{Site,Supplier};
|
||||||
|
|
||||||
|
class ImportCosts extends Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name and signature of the console command.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $signature = 'costs:import {siteid : Site ID} {supplier : Supplier Name} {file : Filename} {date : Date}';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The console command description.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $description = 'Import Costs from file';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the console command.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
Job::dispatchSync(
|
||||||
|
Site::findOrFail($this->argument('siteid')),
|
||||||
|
Supplier::where('name',$this->argument('supplier'))->singleOrFail(),
|
||||||
|
Carbon::create($this->argument('date')),
|
||||||
|
$this->argument('file'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
15
app/Http/Controllers/CostController.php
Normal file
15
app/Http/Controllers/CostController.php
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
use App\Models\Cost;
|
||||||
|
|
||||||
|
class CostController extends Controller
|
||||||
|
{
|
||||||
|
public function home(Cost $o)
|
||||||
|
{
|
||||||
|
return view('a.cost.home',['o'=>$o]);
|
||||||
|
}
|
||||||
|
}
|
228
app/Jobs/ImportCosts.php
Normal file
228
app/Jobs/ImportCosts.php
Normal file
@ -0,0 +1,228 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Jobs;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldBeUnique;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
use Illuminate\Support\Facades\Config;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
use App\Models\{Cost,Service,Site,Supplier};
|
||||||
|
use App\Traits\Import;
|
||||||
|
|
||||||
|
class ImportCosts implements ShouldQueue
|
||||||
|
{
|
||||||
|
use Dispatchable,InteractsWithQueue,Queueable,SerializesModels,Import;
|
||||||
|
|
||||||
|
private const LOGKEY = 'JIC';
|
||||||
|
private Cost $co;
|
||||||
|
private string $file;
|
||||||
|
protected Collection $columns;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new job instance.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function __construct(Site $site,Supplier $so,Carbon $invoice_date,string $file)
|
||||||
|
{
|
||||||
|
$this->file = $file;
|
||||||
|
Config::set('site',$site);
|
||||||
|
|
||||||
|
$this->co = Cost::where('site_id',$site->id)
|
||||||
|
->where('supplier_id',$so->id)
|
||||||
|
->where('billed_at',$invoice_date)
|
||||||
|
->firstOrNew();
|
||||||
|
$this->co->active = TRUE;
|
||||||
|
$this->co->site_id = $site->id;
|
||||||
|
$this->co->billed_at = $invoice_date;
|
||||||
|
$this->co->supplier_id = $so->id;
|
||||||
|
$this->co->save();
|
||||||
|
|
||||||
|
Cost\Broadband::where('cost_id',$this->co->id)->where('site_id',$site->id)->delete();
|
||||||
|
Cost\Phone::where('cost_id',$this->co->id)->where('site_id',$site->id)->delete();
|
||||||
|
Cost\Generic::where('cost_id',$this->co->id)->where('site_id',$site->id)->delete();
|
||||||
|
|
||||||
|
// @todo to be stored in supplier config
|
||||||
|
$headers = [
|
||||||
|
'INVOICEID'=>'Item ID',
|
||||||
|
'REF'=>'Reference No',
|
||||||
|
'IDTAG'=>'ID Tag',
|
||||||
|
'CATEGORY'=>'Category',
|
||||||
|
'DESC'=>'Item Description',
|
||||||
|
'QUANTITY'=>'Quantity',
|
||||||
|
'PRICEUNIT'=>'Unit Price (inc-GST)',
|
||||||
|
'PRICETOTAL'=>'Total (inc-GST)'
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->columns = collect($headers)->transform(function($item) { return strtolower($item); });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the job.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
$skip = 7; // @todo to be stored in supplier config
|
||||||
|
|
||||||
|
$file = fopen($this->file,'r');
|
||||||
|
$haveHeader = FALSE;
|
||||||
|
|
||||||
|
$c = 0;
|
||||||
|
while (! feof($file)) {
|
||||||
|
$line = stream_get_line($file,0,"\r\n");
|
||||||
|
if (str_starts_with($line,'#'))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Remove any embedded CR and BOM
|
||||||
|
$line = str_replace("\r",'',$line);
|
||||||
|
$line = preg_replace('/^\x{feff}/u','',$line);
|
||||||
|
if (($c++ < $skip) || (! $line))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// The first line is a header.
|
||||||
|
if (! $haveHeader) {
|
||||||
|
Log::debug(sprintf('%s: Input File: %s',get_class($this),$this->file));
|
||||||
|
Log::debug(sprintf('%s: Processing columns: %s',get_class($this),join('|',$this->setColumns($line)->toArray())));
|
||||||
|
$haveHeader = TRUE;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the line has a , between two (), then convert the comma to a space.
|
||||||
|
$x = [];
|
||||||
|
if (preg_match('#\(.+,.+\)#i',$line,$x)) {
|
||||||
|
$replace = str_replace(',','_',$x[0]);
|
||||||
|
$line = str_replace($x[0],$replace,$line);
|
||||||
|
//dd($line,$x);
|
||||||
|
}
|
||||||
|
|
||||||
|
$fields = str_getcsv(trim($line));
|
||||||
|
if (is_null($x=$this->getColumnKey('DESC')) OR empty($fields[$x]))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// The first part of our item description is the service number.
|
||||||
|
// This should go to a "supplier" function, since all suppliers may show different values in description.
|
||||||
|
$m = [];
|
||||||
|
$desc = $fields[$x];
|
||||||
|
// m[1] = Service, m[2] = Desc, m[3] = From Date, m[4] = To Date
|
||||||
|
preg_match('#^([0-9]{10})\s+-\s+(.*)\(([0-9]+\s+[JFMASOND].*\s+[0-9]+)+\s+-\s+([0-9]+\s+[JFMASOND].*\s+[0-9]+)+\)$#',$fields[$x],$m);
|
||||||
|
|
||||||
|
if (count($m) !== 5)
|
||||||
|
throw new \Exception(sprintf('ERROR: Description didnt parse [%s] on line [%d]',$fields[$x],$c));
|
||||||
|
|
||||||
|
$cost = ($x=$this->getColumnKey('PRICETOTAL')) ? str_replace([',','$'],'',$fields[$x]) : NULL;
|
||||||
|
$start_at = Carbon::createFromFormat('d M Y',$m[3]);
|
||||||
|
$stop_at = Carbon::createFromFormat('d M Y',$m[4]);
|
||||||
|
$so = Service::search($m[1])->active()->with(['type','product.type.supplied'])->single();
|
||||||
|
|
||||||
|
if ($so) {
|
||||||
|
// r[1] = Monthly Charge or Extra Charge,r[2] = "On Plan", r[3] = Plan Info
|
||||||
|
$r = [];
|
||||||
|
switch ($so->category) {
|
||||||
|
case 'broadband':
|
||||||
|
$to = Cost\Broadband::where('site_id',$this->co->site_id)
|
||||||
|
->where('cost_id',$this->co->id)
|
||||||
|
->where('service_broadband_id',$so->type->id)
|
||||||
|
->where('start_at',$start_at)
|
||||||
|
->where('end_at',$stop_at)
|
||||||
|
->firstOrNew();
|
||||||
|
$to->service_broadband_id = $so->type->id;
|
||||||
|
|
||||||
|
preg_match('#^(Monthly Internet Charge|Plan Change Fee|Change billing date refund for Monthly Internet Charge On Plan|First 12 Month VISP broadband plan discount|.*)\s?(On Plan)?\s?(.*)#',$m[2],$r);
|
||||||
|
|
||||||
|
switch ($r[1]) {
|
||||||
|
case 'Monthly Internet Charge':
|
||||||
|
case 'First 12 Month VISP broadband plan discount':
|
||||||
|
case 'Change billing date refund for Monthly Internet Charge On Plan':
|
||||||
|
$to->base =+ $cost;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'Plan Change Fee':
|
||||||
|
$to->excess =+ $cost;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
dump(['extra charge'=>$r]);
|
||||||
|
$to->excess =+ $cost;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'phone':
|
||||||
|
$to = Cost\Phone::where('site_id',$this->co->site_id)
|
||||||
|
->where('cost_id',$this->co->id)
|
||||||
|
->where('service_phone_id',$so->type->id)
|
||||||
|
->where('start_at',$start_at)
|
||||||
|
->where('end_at',$stop_at)
|
||||||
|
->firstOrNew();
|
||||||
|
$to->service_phone_id = $so->type->id;
|
||||||
|
|
||||||
|
preg_match('#^(Residential VOIP Plan Excess Usage|Virtual FAX Number Monthly Rental|Corporate VOIP Plan Monthly Rental|Residential VOIP Plan Monthly Rental|.*)\s?(.*)#',$m[2],$r);
|
||||||
|
|
||||||
|
switch ($r[1]) {
|
||||||
|
case 'Residential VOIP Plan Monthly Rental':
|
||||||
|
case 'Virtual FAX Number Monthly Rental':
|
||||||
|
case 'Corporate VOIP Plan Monthly Rental':
|
||||||
|
$to->base =+ $cost;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'Residential VOIP Plan Excess Usage':
|
||||||
|
$to->excess =+ $cost;
|
||||||
|
$to->notes = $r[2];
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
dump(['extra charge'=>$r]);
|
||||||
|
$to->excess =+ $cost;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
dump(['so'=>$so,'line'=>$line]);
|
||||||
|
throw new \Exception(sprintf('ERROR: Service type not handled for service [%s] (%s) on line [%d]',$m[1],$so->category,$c));
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
dump(['line'=>$line,'sql'=>Service::search($m[1])->active()->with(['type','product.type.supplied'])->toSql()]);
|
||||||
|
|
||||||
|
$to = Cost\Generic::where('site_id',$this->co->site_id)
|
||||||
|
->where('cost_id',$this->co->id)
|
||||||
|
->where('notes',sprintf('%s:%s',$m[1],$m[2]))
|
||||||
|
->where('start_at',$start_at)
|
||||||
|
->where('end_at',$stop_at)
|
||||||
|
->firstOrNew();
|
||||||
|
|
||||||
|
$to->excess =+ $cost;
|
||||||
|
$to->notes = $line;
|
||||||
|
}
|
||||||
|
|
||||||
|
$to->site_id = $this->co->site_id;
|
||||||
|
$to->cost_id = $this->co->id;
|
||||||
|
$to->active = TRUE;
|
||||||
|
$to->start_at = $start_at;
|
||||||
|
$to->end_at = $stop_at;
|
||||||
|
|
||||||
|
// Work out supplier product number
|
||||||
|
Log::warning(sprintf('%s:Supplier product ID not matched',self::LOGKEY),['r'=>$r]);
|
||||||
|
|
||||||
|
//dd($m[2],$cost,$so->product->type->supplied);
|
||||||
|
|
||||||
|
// Work out if this base charge, or extra charge
|
||||||
|
|
||||||
|
//dd(['M'=>__METHOD__,'fields'=>$fields,'DESC'=>$this->getColumnKey('DESC'),'desc'=>$desc,'m'=>$m,'sql'=>$so->toSql(),'bindings'=>$so->getBindings(),'so'=>$so]);
|
||||||
|
$to->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose($file);
|
||||||
|
}
|
||||||
|
}
|
40
app/Models/Cost.php
Normal file
40
app/Models/Cost.php
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
use App\Models\Cost\{Broadband,Generic,Phone};
|
||||||
|
|
||||||
|
class Cost extends Model
|
||||||
|
{
|
||||||
|
use HasFactory;
|
||||||
|
|
||||||
|
protected $dates = [
|
||||||
|
'billed_at',
|
||||||
|
];
|
||||||
|
|
||||||
|
/* RELATIONS */
|
||||||
|
|
||||||
|
public function broadbands()
|
||||||
|
{
|
||||||
|
return $this->hasMany(Broadband::class)
|
||||||
|
->where('active',TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function generics()
|
||||||
|
{
|
||||||
|
return $this->hasMany(Generic::class)
|
||||||
|
->where('active',TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function phones()
|
||||||
|
{
|
||||||
|
return $this->hasMany(Phone::class)
|
||||||
|
->where('active',TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ATTRIBUTES */
|
||||||
|
|
||||||
|
}
|
18
app/Models/Cost/Broadband.php
Normal file
18
app/Models/Cost/Broadband.php
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models\Cost;
|
||||||
|
|
||||||
|
use App\Models\Service;
|
||||||
|
use App\Models\Service\Broadband as BroadbandService;
|
||||||
|
|
||||||
|
class Broadband extends Type
|
||||||
|
{
|
||||||
|
protected $table = 'cost_broadband';
|
||||||
|
|
||||||
|
/* RELATIONS */
|
||||||
|
|
||||||
|
public function service()
|
||||||
|
{
|
||||||
|
return $this->hasOneThrough(Service::class,BroadbandService::class,'id','id','service_broadband_id','service_id');
|
||||||
|
}
|
||||||
|
}
|
18
app/Models/Cost/Generic.php
Normal file
18
app/Models/Cost/Generic.php
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models\Cost;
|
||||||
|
|
||||||
|
use App\Models\Service;
|
||||||
|
use App\Models\Service\Generic as GenericService;
|
||||||
|
|
||||||
|
class Generic extends Type
|
||||||
|
{
|
||||||
|
protected $table = 'cost_generic';
|
||||||
|
|
||||||
|
/* RELATIONS */
|
||||||
|
|
||||||
|
public function service()
|
||||||
|
{
|
||||||
|
return $this->hasOneThrough(Service::class,GenericService::class,'id','id','service_generic_id','service_id');
|
||||||
|
}
|
||||||
|
}
|
18
app/Models/Cost/Phone.php
Normal file
18
app/Models/Cost/Phone.php
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models\Cost;
|
||||||
|
|
||||||
|
use App\Models\Service;
|
||||||
|
use App\Models\Service\Phone as PhoneService;
|
||||||
|
|
||||||
|
class Phone extends Type
|
||||||
|
{
|
||||||
|
protected $table = 'cost_phone';
|
||||||
|
|
||||||
|
/* RELATIONS */
|
||||||
|
|
||||||
|
public function service()
|
||||||
|
{
|
||||||
|
return $this->hasOneThrough(Service::class,PhoneService::class,'id','id','service_phone_id','service_id');
|
||||||
|
}
|
||||||
|
}
|
26
app/Models/Cost/Type.php
Normal file
26
app/Models/Cost/Type.php
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models\Cost;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
abstract class Type extends Model
|
||||||
|
{
|
||||||
|
public $timestamps = FALSE;
|
||||||
|
|
||||||
|
protected $dates = [
|
||||||
|
'start_at',
|
||||||
|
'end_at',
|
||||||
|
];
|
||||||
|
|
||||||
|
/* RELATIONS */
|
||||||
|
|
||||||
|
abstract public function service();
|
||||||
|
|
||||||
|
/* ATTRIBUTES */
|
||||||
|
|
||||||
|
public function getCostAttribute(): float
|
||||||
|
{
|
||||||
|
return $this->base+$this->excess;
|
||||||
|
}
|
||||||
|
}
|
17
app/Models/Policies/CostPolicy.php
Normal file
17
app/Models/Policies/CostPolicy.php
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models\Policies;
|
||||||
|
|
||||||
|
use Illuminate\Auth\Access\HandlesAuthorization;
|
||||||
|
|
||||||
|
use App\Models\{Cost,User};
|
||||||
|
|
||||||
|
class CostPolicy
|
||||||
|
{
|
||||||
|
use HandlesAuthorization;
|
||||||
|
|
||||||
|
public function view(User $uo,Cost $co): bool
|
||||||
|
{
|
||||||
|
return $uo->isWholesaler();
|
||||||
|
}
|
||||||
|
}
|
43
app/Traits/Import.php
Normal file
43
app/Traits/Import.php
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Import from files
|
||||||
|
*/
|
||||||
|
namespace App\Traits;
|
||||||
|
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
|
||||||
|
trait Import
|
||||||
|
{
|
||||||
|
protected Collection $_columns;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the columns from the file that we'll work with.
|
||||||
|
* This creates an index of the position of each header in the file, which we use to find an value by getColumnKey()
|
||||||
|
*
|
||||||
|
* @param string $line
|
||||||
|
* @return Collection
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
private function setColumns(string $line): Collection
|
||||||
|
{
|
||||||
|
// If columns is not set, then this is an error
|
||||||
|
if (! $this->columns)
|
||||||
|
throw new \Exception('ERROR: Columns must be set before calling setColumns()');
|
||||||
|
|
||||||
|
$this->_columns = collect(explode(',',strtolower($line)))->filter();
|
||||||
|
|
||||||
|
return $this->_columns->intersect($this->columns);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the index for the column in the file
|
||||||
|
*
|
||||||
|
* @param string $key
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
private function getColumnKey(string $key)
|
||||||
|
{
|
||||||
|
return $this->_columns->search($this->columns->get($key));
|
||||||
|
}
|
||||||
|
}
|
101
database/migrations/2022_06_13_230224_costs.php
Normal file
101
database/migrations/2022_06_13_230224_costs.php
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
$this->down();
|
||||||
|
Schema::create('costs', function (Blueprint $table) {
|
||||||
|
$table->integer('id',TRUE,TRUE);
|
||||||
|
$table->timestamps();
|
||||||
|
$table->integer('site_id')->unsigned();
|
||||||
|
$table->date('billed_at');
|
||||||
|
$table->boolean('active');
|
||||||
|
$table->integer('supplier_id')->unsigned();
|
||||||
|
$table->string('invoice_num')->nullable();
|
||||||
|
|
||||||
|
$table->unique(['id','site_id']);
|
||||||
|
$table->foreign(['site_id'])->references(['site_id'])->on('sites');
|
||||||
|
$table->foreign(['supplier_id'])->references(['id'])->on('suppliers');
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::create('cost_broadband', function (Blueprint $table) {
|
||||||
|
$table->integer('id',TRUE,TRUE);
|
||||||
|
$table->integer('site_id')->unsigned();
|
||||||
|
$table->integer('cost_id')->unsigned();
|
||||||
|
$table->boolean('active');
|
||||||
|
$table->integer('service_broadband_id')->unsigned()->nullable();
|
||||||
|
$table->integer('supplier_broadband_id')->unsigned()->nullable();
|
||||||
|
$table->date('start_at')->nullable();
|
||||||
|
$table->date('end_at')->nullable();
|
||||||
|
$table->float('base')->nullable();
|
||||||
|
$table->float('excess')->nullable();
|
||||||
|
$table->string('reference')->nullable();
|
||||||
|
$table->text('notes')->nullable();
|
||||||
|
|
||||||
|
$table->foreign(['cost_id','site_id'])->references(['id','site_id'])->on('costs');
|
||||||
|
$table->foreign(['service_broadband_id','site_id'])->references(['id','site_id'])->on('service_broadband');
|
||||||
|
$table->foreign(['supplier_broadband_id','site_id'])->references(['id','site_id'])->on('supplier_broadband');
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::create('cost_phone', function (Blueprint $table) {
|
||||||
|
$table->integer('id',TRUE,TRUE);
|
||||||
|
$table->integer('site_id')->unsigned();
|
||||||
|
$table->integer('cost_id')->unsigned();
|
||||||
|
$table->boolean('active');
|
||||||
|
$table->integer('service_phone_id')->unsigned()->nullable();
|
||||||
|
$table->integer('supplier_phone_id')->unsigned()->nullable();
|
||||||
|
$table->date('start_at')->nullable();
|
||||||
|
$table->date('end_at')->nullable();
|
||||||
|
$table->float('base')->nullable();
|
||||||
|
$table->float('excess')->nullable();
|
||||||
|
$table->string('reference')->nullable();
|
||||||
|
$table->text('notes')->nullable();
|
||||||
|
|
||||||
|
$table->foreign(['cost_id','site_id'])->references(['id','site_id'])->on('costs');
|
||||||
|
$table->foreign(['service_phone_id','site_id'])->references(['id','site_id'])->on('service_phone');
|
||||||
|
$table->foreign(['supplier_phone_id','site_id'])->references(['id','site_id'])->on('supplier_phone');
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::create('cost_generic', function (Blueprint $table) {
|
||||||
|
$table->integer('id',TRUE,TRUE);
|
||||||
|
$table->integer('site_id')->unsigned();
|
||||||
|
$table->integer('cost_id')->unsigned();
|
||||||
|
$table->boolean('active');
|
||||||
|
$table->integer('service_generic_id')->unsigned()->nullable();
|
||||||
|
$table->integer('supplier_generic_id')->unsigned()->nullable();
|
||||||
|
$table->date('start_at')->nullable();
|
||||||
|
$table->date('end_at')->nullable();
|
||||||
|
$table->float('base')->nullable();
|
||||||
|
$table->float('excess')->nullable();
|
||||||
|
$table->string('reference')->nullable();
|
||||||
|
$table->text('notes')->nullable();
|
||||||
|
|
||||||
|
$table->foreign(['cost_id','site_id'])->references(['id','site_id'])->on('costs');
|
||||||
|
$table->foreign(['service_generic_id','site_id'])->references(['id','site_id'])->on('service_generic');
|
||||||
|
$table->foreign(['supplier_generic_id','site_id'])->references(['id','site_id'])->on('supplier_generic');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('cost_generic');
|
||||||
|
Schema::dropIfExists('cost_phone');
|
||||||
|
Schema::dropIfExists('cost_broadband');
|
||||||
|
Schema::dropIfExists('costs');
|
||||||
|
}
|
||||||
|
};
|
165
resources/views/theme/backend/adminlte/a/cost/home.blade.php
Normal file
165
resources/views/theme/backend/adminlte/a/cost/home.blade.php
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
@extends('adminlte::layouts.app')
|
||||||
|
|
||||||
|
@section('htmlheader_title')
|
||||||
|
Cost #{{ $o->id }}
|
||||||
|
@endsection
|
||||||
|
|
||||||
|
@section('contentheader_title')
|
||||||
|
Cost #{{ $o->id }}
|
||||||
|
@endsection
|
||||||
|
@section('contentheader_description')
|
||||||
|
|
||||||
|
@endsection
|
||||||
|
|
||||||
|
<!-- $o = App\Models\Costs -->
|
||||||
|
@php($cost = 0)
|
||||||
|
@php($charge = 0)
|
||||||
|
@section('main-content')
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-6">
|
||||||
|
<div class="card card-dark">
|
||||||
|
<div class="card-body">
|
||||||
|
<table class="table table-striped" id="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Category</th>
|
||||||
|
<th>Service</th>
|
||||||
|
<th>Active</th>
|
||||||
|
<th>From</th>
|
||||||
|
<th>To</th>
|
||||||
|
<th class="text-right">Base</th>
|
||||||
|
<th class="text-right">Excess</th>
|
||||||
|
<th class="text-right">Cost</th>
|
||||||
|
<th class="text-right">Charge</th>
|
||||||
|
<th class="text-right">Profit</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody>
|
||||||
|
|
||||||
|
<!-- Broadband -->
|
||||||
|
<tr>
|
||||||
|
<td>Broadband</td>
|
||||||
|
<td> </td>
|
||||||
|
<td> </td>
|
||||||
|
<td>{{ $o->broadbands->min('start_at')->format('Y-m-d') }}</td>
|
||||||
|
<td>{{ $o->broadbands->max('end_at')->format('Y-m-d') }}</td>
|
||||||
|
<td class="text-right">${{ number_format($a=$o->broadbands->sum('base'),2) }}</td>
|
||||||
|
<td class="text-right">${{ number_format($o->broadbands->sum('excess'),2) }}</td>
|
||||||
|
<td class="text-right">${{ number_format($o->broadbands->sum('cost'),2) }}</td>
|
||||||
|
<td class="text-right">${{ number_format($b=$o->broadbands->sum('service.billing_monthly_price'),2) }}</td>
|
||||||
|
<td class="text-right {{ $b-$a < 0 ? 'text-danger' : '' }}">${{ number_format($b-$a,2) }}</td>
|
||||||
|
</tr>
|
||||||
|
@php($cost += $a)
|
||||||
|
@php($charge += $b)
|
||||||
|
|
||||||
|
@foreach ($o->broadbands->groupBy('service_broadband_id') as $oo)
|
||||||
|
<tr>
|
||||||
|
<td> </td>
|
||||||
|
<td>{{ $oo->first()->service->name }}</td>
|
||||||
|
<td>{{ $oo->first()->service->active ? 'YES' : 'NO' }}</td>
|
||||||
|
<td>{{ $oo->min('start_at')->format('Y-m-d') }}</td>
|
||||||
|
<td>{{ $oo->max('end_at')->format('Y-m-d') }}</td>
|
||||||
|
<td class="text-right">${{ number_format($a=$oo->sum('base'),2) }}</td>
|
||||||
|
<td class="text-right">${{ number_format($oo->sum('excess'),2) }}</td>
|
||||||
|
<td class="text-right">${{ number_format($oo->sum('cost'),2) }}</td>
|
||||||
|
<td class="text-right">${{ number_format($b=$oo->first()->service->billing_monthly_price,2) }}</td>
|
||||||
|
<td class="text-right {{ $b-$a < 0 ? 'text-danger' : '' }}">${{ number_format($b-$a,2) }}</td>
|
||||||
|
</tr>
|
||||||
|
@endforeach
|
||||||
|
|
||||||
|
<!-- Phone -->
|
||||||
|
<tr>
|
||||||
|
<td>Phone</td>
|
||||||
|
<td> </td>
|
||||||
|
<td> </td>
|
||||||
|
<td>{{ $o->phones->min('start_at')->format('Y-m-d') }}</td>
|
||||||
|
<td>{{ $o->phones->max('end_at')->format('Y-m-d') }}</td>
|
||||||
|
<td class="text-right">${{ number_format($a=$o->phones->sum('base'),2) }}</td>
|
||||||
|
<td class="text-right">${{ number_format($o->phones->sum('excess'),2) }}</td>
|
||||||
|
<td class="text-right">${{ number_format($o->phones->sum('cost'),2) }}</td>
|
||||||
|
<td class="text-right">${{ number_format($b=$o->phones->sum('service.billing_monthly_price'),2) }}</td>
|
||||||
|
<td class="text-right {{ $b-$a < 0 ? 'text-danger' : '' }}">${{ number_format($b-$a,2) }}</td>
|
||||||
|
</tr>
|
||||||
|
@php($cost += $a)
|
||||||
|
@php($charge += $b)
|
||||||
|
|
||||||
|
@foreach ($o->phones->groupBy('service_phone_id') as $oo)
|
||||||
|
<tr>
|
||||||
|
<td> </td>
|
||||||
|
<td>{{ $oo->first()->service->name }}</td>
|
||||||
|
<td>{{ $oo->first()->service->active ? 'YES' : 'NO' }}</td>
|
||||||
|
<td>{{ $oo->min('start_at')->format('Y-m-d') }}</td>
|
||||||
|
<td>{{ $oo->max('end_at')->format('Y-m-d') }}</td>
|
||||||
|
<td class="text-right">${{ number_format($a=$oo->sum('base'),2) }}</td>
|
||||||
|
<td class="text-right">${{ number_format($oo->sum('excess'),2) }}</td>
|
||||||
|
<td class="text-right">${{ number_format($oo->sum('cost'),2) }}</td>
|
||||||
|
<td class="text-right">${{ number_format($b=$oo->first()->service->billing_monthly_price,2) }}</td>
|
||||||
|
<td class="text-right {{ $b-$a < 0 ? 'text-danger' : '' }}">${{ number_format($b-$a,2) }}</td>
|
||||||
|
</tr>
|
||||||
|
@endforeach
|
||||||
|
|
||||||
|
<!-- Generic -->
|
||||||
|
<tr>
|
||||||
|
<td>Generic</td>
|
||||||
|
<td> </td>
|
||||||
|
<td> </td>
|
||||||
|
<td>{{ $o->generics->min('start_at')->format('Y-m-d') }}</td>
|
||||||
|
<td>{{ $o->generics->max('end_at')->format('Y-m-d') }}</td>
|
||||||
|
<td class="text-right">${{ number_format($a=$o->generics->sum('base'),2) }}</td>
|
||||||
|
<td class="text-right">${{ number_format($o->generics->sum('excess'),2) }}</td>
|
||||||
|
<td class="text-right">${{ number_format($o->generics->sum('cost'),2) }}</td>
|
||||||
|
<td class="text-right">$0.00</td>
|
||||||
|
<td class="text-right {{ 0-$a < 0 ? 'text-danger' : '' }}">${{ number_format(0-$a,2) }}</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
@php($cost += $a)
|
||||||
|
@php($charge += $b)
|
||||||
|
|
||||||
|
@foreach ($o->generics->groupBy('service_generic_id') as $oo)
|
||||||
|
<tr>
|
||||||
|
<td> </td>
|
||||||
|
<td>{{ ($x=$oo->first()->service) ? $x->name : '-' }}</td>
|
||||||
|
<td>{{ $x ? ($x->active ? 'YES' : 'NO') : '-' }}</td>
|
||||||
|
<td>{{ $oo->min('start_at')->format('Y-m-d') }}</td>
|
||||||
|
<td>{{ $oo->max('end_at')->format('Y-m-d') }}</td>
|
||||||
|
<td class="text-right">${{ number_format($a=$oo->sum('base'),2) }}</td>
|
||||||
|
<td class="text-right">${{ number_format($oo->sum('excess'),2) }}</td>
|
||||||
|
<td class="text-right">${{ number_format($oo->sum('cost'),2) }}</td>
|
||||||
|
<td class="text-right">${{ number_format($b=$x ? $x->billing_monthly_price : 0,2) }}</td>
|
||||||
|
<td class="text-right {{ $b-$a < 0 ? 'text-danger' : '' }}">${{ number_format($b-$a,2) }}</td>
|
||||||
|
</tr>
|
||||||
|
@endforeach
|
||||||
|
|
||||||
|
</tbody>
|
||||||
|
<tfoot>
|
||||||
|
<tr>
|
||||||
|
<th colspan="7">TOTAL</th>
|
||||||
|
<th class="text-right">${{ number_format($cost,2) }}</th>
|
||||||
|
<th class="text-right">${{ number_format($charge,2) }}</th>
|
||||||
|
<td class="text-right {{ $charge-$cost < 0 ? 'text-danger' : '' }}">${{ number_format($charge-$cost,2) }}</td>
|
||||||
|
</tr>
|
||||||
|
</tfoot>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endsection
|
||||||
|
|
||||||
|
@section('page-scripts')
|
||||||
|
@css(datatables,bootstrap4|rowgroup)
|
||||||
|
@js(datatables,bootstrap4|rowgroup)
|
||||||
|
|
||||||
|
<script>
|
||||||
|
$(document).ready(function() {
|
||||||
|
$('#table').DataTable({
|
||||||
|
order: [[0,'asc'],[1,'desc']],
|
||||||
|
});
|
||||||
|
|
||||||
|
$('tbody').on('click','tr', function () {
|
||||||
|
$(this).toggleClass('selected');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
@append
|
@ -4,6 +4,7 @@ use Leenooks\Controllers\AdminController as LeenooksAdminController;
|
|||||||
use App\Http\Controllers\{AdminController,
|
use App\Http\Controllers\{AdminController,
|
||||||
Auth\LoginController,
|
Auth\LoginController,
|
||||||
Auth\SocialLoginController,
|
Auth\SocialLoginController,
|
||||||
|
CostController,
|
||||||
CheckoutController,
|
CheckoutController,
|
||||||
HomeController,
|
HomeController,
|
||||||
MediaController,
|
MediaController,
|
||||||
@ -67,6 +68,10 @@ Route::group(['middleware'=>['theme:adminlte-be','auth','role:wholesaler'],'pref
|
|||||||
->where('o','[0-9]+')
|
->where('o','[0-9]+')
|
||||||
->middleware('can:update,o');
|
->middleware('can:update,o');
|
||||||
//Route::get('accounting/connect','AccountingController@connect');
|
//Route::get('accounting/connect','AccountingController@connect');
|
||||||
|
|
||||||
|
Route::get('cost/{o}',[CostController::class,'home'])
|
||||||
|
->where('o','[0-9]+')
|
||||||
|
->middleware('can:view,o');
|
||||||
});
|
});
|
||||||
|
|
||||||
Route::get('admin/switch/stop',[LeenooksAdminController::class,'user_switch_stop'])->name('switch.user.start')->middleware('auth');
|
Route::get('admin/switch/stop',[LeenooksAdminController::class,'user_switch_stop'])->name('switch.user.start')->middleware('auth');
|
||||||
|
Loading…
Reference in New Issue
Block a user