@ -2,135 +2,160 @@
namespace App\Console\Commands ;
use DB ;
use Throwable ;
use Carbon\Carbon ;
use App\Models\Cost ;
use App\Models\Business ;
use Illuminate\Console\Command ;
use Morilog\Jalali\CalendarUtils ;
use Illuminate\Support\Facades\DB ;
use Illuminate\Support\Facades\Cache ;
use Illuminate\Support\Facades\Storage ;
class CostCommand extends Command
{
public const USER_FEE = 400 ;
public const FILE_FEE = [
200 => 0 ,
400 => 1000 ,
800 => 2000 ,
];
protected $signature = 'cost:work' ;
protected $description = 'Run the cost worker' ;
public function __construct ()
{
parent :: __construct ();
}
public function handle ()
{
// infinte loop
while ( true ) {
// to baghali ha
$recorded_month = jdate ( $business -> calculated_at ) -> format ( " Y-m-01 " );
$calculated_at = Cache :: get ( 'calculated_at' );
if ( $calculated_at === null ) {
$business = Business :: orderBy ( 'calculated_at' ) -> first () -> load ( 'users' , 'cost' );
$calculated_at = $business -> calculated_at ;
$until_now = jdate () -> getMonth () > jdate ( $business -> calculated_at ) -> getMonth ()
? jdate ( $business -> calculated_at ) -> toCarbon () -> setTime ( " 00 " , " 00 " , " 00 " )
: \Carbon\Carbon :: now ();
Cache :: put ( 'calculated_at' , $until_now , 60 );
$lock = Cache :: get ( 'lock' , false );
if ( $lock ) {
$this -> info ( 'There is no business for auditing.' );
continue ;
}
$business = Business :: orderBy ( 'calculated_at' ) -> first ();
if ( $business === null ) {
continue ;
}
if ( $business -> calculated_at -> isFuture ()) {
continue ;
}
$next_month = jdate ( $business -> calculated_at ) -> addMonths ();
[ $year , $month , $day ] = CalendarUtils :: toGregorian (
$next_month -> getYear (), $next_month -> getMonth (), 1
);
$now = Carbon :: createFromDate ( $year , $month , $day );
$now -> setTime ( 0 , 0 , 0 );
if ( $now -> isFuture ()) {
$now = Carbon :: now ();
}
// if calculated_at less than an hour stop
if ( \Carbon\Carbon :: now () -> diffInMinutes ( $until_now ) <= 60 ) {
$this -> info ( 'nothing to cost' );
if ( $now -> diffInMinutes ( $business -> calculated_at ) <= 59 ) {
$this -> info ( 'Must be one hour after the last audit.' );
Cache :: put ( 'lock' , true , $now -> diffInSeconds ( $business -> calculated_at ));
continue ;
}
try {
DB :: beginTransaction ();
// Fixed amounts of expenses
$business -> load ( 'users' , 'files' );
// business order by last_calculated_at take first
if ( ! isset ( $business )) {
$business = Business :: orderBy ( 'calculated_at' ) -> first () -> load ( 'users' , 'cost' );
}
$user_fee = enum ( 'business.fee.user' );
// get business employee
$users_cost = $business -> cost
-> where ( 'type' , '=' , 'users' )
-> where ( 'fee' , '=' , $user_fee )
-> where ( 'month' , '=' , $recorded_month )
-> where ( 'amount' , '=' , $business -> users -> count ())
-> first ();
if ( $users_cost === null ) {
$business -> cost () -> create ([
'type' => 'users' ,
'month' => $recorded_month ,
'amount' => $business -> users -> count (),
'fee' => $user_fee ,
'duration' => $duration = $until_now -> diffInSeconds ( $calculated_at ), // from the created_at time of the newset fifth user
'additional' => $business -> users -> pluck ( 'id' ) -> toArray (),
]);
} else {
$users_cost -> update ([
'duration' => $duration = $until_now -> diffInMinutes ( $calculated_at ), // last calc - (current month - now else last calc - end of the past month),
'additional' => $business -> users -> pluck ( 'id' ) -> toArray (),
]);
}
$costs = $user_fee * $duration ;
// do the math in php
if ( intdiv ( $business -> files_volume , 200 ) === 0 ) {
$pads = 0 ;
} else {
$pads = intdiv ( $business -> files_volume , 200 ) - 1 ;
}
$file_fee = enum ( 'business.fee.file' );
$files = $business -> cost
-> where ( 'type' , '=' , 'files' )
-> where ( 'fee' , '=' , $file_fee )
-> where ( 'month' , '=' , $recorded_month )
-> where ( 'amount' , '=' , $business -> files_volume )
-> first ();
if ( $files === null ) {
$business -> cost () -> create ([
'type' => 'files' ,
'month' => $recorded_month ,
'amount' => $pads ,
'fee' => $file_fee ,
'duration' => $duration = $until_now -> diffInMinutes ( $calculated_at ), // how to determine the file?,
]);
} else {
$files -> update ([
'duration' => $duration = $until_now -> diffInMinutes ( $calculated_at ), // last calc - (current month - now else last calc - end of the past month),,
]);
}
$costs += $file_fee * $duration ;
$costs = 0 ;
$costs += $this -> calculateCostOfBusinessUsers ( $business , $now );
$costs += $this -> calculateCostOfBusinessFiles ( $business , $now );
// increment and decrement of wallet in php
// deduct costs from your business wallet
// make sure save the calculated_at
$business -> update ([
'wallet' => $business -> wallet - $costs ,
'calculated_at' => \Carbon\Carbon :: now () ,
'calculated_at' => $now ,
]);
DB :: commit ();
} catch ( Throwable $thr ) {
$this -> info ( " The business # { $business -> id } was audited. " );
} catch ( Throwable $throwable ) {
throw $throwable ;
DB :: rollback ();
throw $thr ;
report ( $throwable );
continue ;
}
}
}
public function calculateCostOfBusinessUsers ( $business , $now )
{
$user_fee = enum ( 'business.fee.user' );
$recorded_month = jdate ( $business -> calculated_at ) -> format ( " Y-m-01 " );
if ( $business -> users -> isEmpty ()) {
return 0 ;
}
// get business employee
$users_cost = $business -> cost
-> where ( 'type' , '=' , Cost :: USER_TYPE )
-> where ( 'fee' , '=' , $user_fee )
-> where ( 'month' , '=' , $recorded_month )
-> where ( 'amount' , '=' , $business -> users -> count ())
-> first ();
if ( $users_cost === null ) {
$business -> cost () -> create ([
'type' => Cost :: USER_TYPE ,
'month' => $recorded_month ,
'amount' => $business -> users -> count (),
'fee' => $user_fee ,
'duration' => $duration = $now -> diffInMinutes ( $business -> calculated_at ), // from the created_at time of the newset fifth user
'additional' => $business -> users -> pluck ( 'id' ) -> toArray (),
]);
} else {
$users_cost -> update ([
'duration' => $duration = $now -> diffInMinutes ( $business -> calculated_at ) + $users_cost -> duration , // last calc - (current month - now else last calc - end of the past month),
'additional' => $business -> users -> pluck ( 'id' ) -> toArray (),
]);
}
return $user_fee * $duration ;
}
public function calculateCostOfBusinessFiles ( $business , $now )
{
$file_fee = enum ( 'business.fee.file' );
$calculated_at = $business -> calculated_at ;
$recorded_month = jdate ( $business -> calculated_at ) -> format ( " Y-m-01 " );
// do the math in php
$packs = intdiv ( $business -> files_volume , 200 );
if ( $packs === 0 ) {
return 0 ;
} else {
$packs -- ;
}
$files = $business -> cost
-> where ( 'type' , '=' , Cost :: FILE_TYPE )
-> where ( 'fee' , '=' , $file_fee )
-> where ( 'month' , '=' , $recorded_month )
-> where ( 'amount' , '=' , $packs )
-> first ();
if ( $files === null ) {
$business -> cost () -> create ([
'type' => Cost :: FILE_TYPE ,
'month' => $recorded_month ,
'amount' => $packs ,
'fee' => $file_fee ,
'duration' => $duration = $now -> diffInMinutes ( $calculated_at ), // how to determine the file?,
'additional' => [ 'volume' => $business -> files_volume ],
]);
} else {
$files -> update ([
'duration' => $duration = $now -> diffInMinutes ( $calculated_at ) + $files -> duration , // last calc - (current month - now else last calc - end of the past month),,
'additional' => [ 'volume' => $business -> files_volume ],
]);
}
return $file_fee * $duration ;
}
}