Merge branch 'master' into title-subheading

This commit is contained in:
Cüneyt Şentürk
2023-07-19 14:43:19 +03:00
committed by GitHub
628 changed files with 65799 additions and 80258 deletions

View File

@@ -15,9 +15,10 @@ use Maatwebsite\Excel\Concerns\ShouldAutoSize;
use Maatwebsite\Excel\Concerns\WithHeadings;
use Maatwebsite\Excel\Concerns\WithMapping;
use Maatwebsite\Excel\Concerns\WithTitle;
use Maatwebsite\Excel\Concerns\WithStrictNullComparison;
use PhpOffice\PhpSpreadsheet\Shared\Date as ExcelDate;
abstract class Export implements FromCollection, HasLocalePreference, ShouldAutoSize, ShouldQueue, WithHeadings, WithMapping, WithTitle
abstract class Export implements FromCollection, HasLocalePreference, ShouldAutoSize, ShouldQueue, WithHeadings, WithMapping, WithTitle, WithStrictNullComparison
{
use Exportable;
@@ -48,13 +49,18 @@ abstract class Export implements FromCollection, HasLocalePreference, ShouldAuto
{
$map = [];
$date_fields = ['paid_at', 'invoiced_at', 'billed_at', 'due_at', 'issued_at', 'created_at', 'transferred_at'];
$date_fields = ['paid_at', 'invoiced_at', 'billed_at', 'due_at', 'issued_at', 'transferred_at'];
$evil_chars = ['=', '+', '-', '@'];
foreach ($this->fields as $field) {
$value = $model->$field;
// created_by is equal to the owner id. Therefore, the value in export is owner email.
if ($field == 'created_by') {
$value = $model->owner->email ?? null;
}
if (in_array($field, $date_fields)) {
$value = ExcelDate::PHPToExcel(Date::parse($value)->format('Y-m-d'));
}

View File

@@ -2,6 +2,7 @@
namespace App\Abstracts;
use App\Abstracts\Http\FormRequest;
use App\Traits\Import as ImportHelper;
use App\Traits\Sources;
use App\Utilities\Date;
@@ -11,6 +12,7 @@ use Illuminate\Contracts\Translation\HasLocalePreference;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException;
use Maatwebsite\Excel\Concerns\Importable;
use Maatwebsite\Excel\Concerns\SkipsEmptyRows;
use Maatwebsite\Excel\Concerns\ToModel;
@@ -27,6 +29,8 @@ abstract class Import implements HasLocalePreference, ShouldQueue, SkipsEmptyRow
public $user;
public $request_class = null;
public function __construct()
{
$this->user = user();
@@ -35,7 +39,12 @@ abstract class Import implements HasLocalePreference, ShouldQueue, SkipsEmptyRow
public function map($row): array
{
$row['company_id'] = company_id();
$row['created_by'] = $this->user->id;
// created_by is equal to the owner id. Therefore, the value in export is owner email.
if (isset($row['created_by'])) {
$row['created_by'] = $this->getCreatedById($row);
}
$row['created_from'] = $this->getSourcePrefix() . 'import';
// Make enabled field integer
@@ -48,7 +57,7 @@ abstract class Import implements HasLocalePreference, ShouldQueue, SkipsEmptyRow
$row['reconciled'] = (int) $row['reconciled'];
}
$date_fields = ['paid_at', 'invoiced_at', 'billed_at', 'due_at', 'issued_at', 'created_at', 'transferred_at'];
$date_fields = ['paid_at', 'invoiced_at', 'billed_at', 'due_at', 'issued_at', 'transferred_at'];
foreach ($date_fields as $date_field) {
if (!isset($row[$date_field])) {
continue;
@@ -70,6 +79,49 @@ abstract class Import implements HasLocalePreference, ShouldQueue, SkipsEmptyRow
return [];
}
/**
* You can override this method to add custom rules for each row.
*/
public function prepareRules(array $rules): array
{
return $rules;
}
/**
* Validate each row data.
*
* @param \Illuminate\Validation\Validator $validator
* @throws ValidationException
*/
public function withValidator($validator)
{
$condition = class_exists($this->request_class)
? ! ($request = new $this->request_class) instanceof FormRequest
: true;
if ($condition) {
return;
}
foreach ($validator->getData() as $row => $data) {
$request->initialize(request: $data);
$rules = $this->prepareRules($request->rules());
try {
Validator::make($data, $rules)->validate();
} catch (ValidationException $e) {
foreach ($e->validator->failed() as $attribute => $value) {
foreach ($value as $rule => $params) {
$validator->addFailure($row . '.' . $attribute, $rule, $params);
}
}
throw new ValidationException($validator);
}
}
}
public function chunkSize(): int
{
return config('excel.imports.chunk_size');

View File

@@ -20,6 +20,6 @@ abstract class ImportMultipleSheets implements ShouldQueue, WithChunkReading, Wi
public function chunkSize(): int
{
return 100;
return config('excel.imports.chunk_size');
}
}

View File

@@ -22,10 +22,10 @@ abstract class Model extends Eloquent implements Ownable
protected $tenantable = true;
protected $dates = ['deleted_at'];
protected $casts = [
'enabled' => 'boolean',
'amount' => 'double',
'enabled' => 'boolean',
'deleted_at' => 'datetime',
];
public $allAttributes = [];
@@ -105,7 +105,7 @@ abstract class Model extends Eloquent implements Ownable
/**
* Modules that use the sort parameter in CRUD operations cause an error,
* so this sort parameter set back to old value after the query is executed.
*
*
* for Custom Fields module
*/
$request_sort = $request->get('sort');
@@ -117,7 +117,8 @@ abstract class Model extends Eloquent implements Ownable
}
$request->merge(['sort' => $request_sort]);
$request->offsetUnset('direction');
// This line disabled because broken sortable issue.
//$request->offsetUnset('direction');
$limit = (int) $request->get('limit', setting('default.list_limit', '25'));
return $query->paginate($limit);

View File

@@ -222,7 +222,9 @@ abstract class Report
foreach ($tmp_values as $id => $value) {
$labels[$id] = $this->row_names[$table_key][$id];
$colors[$id] = ($group == 'category') ? Category::withSubCategory()->find($id)?->colorHexCode : '#' . dechex(rand(0x000000, 0xFFFFFF));
$colors[$id] = ($group == 'category')
? Category::withSubCategory()->find($id)?->colorHexCode
: $this->randHexColor();
$values[$id] = round(($value * 100 / $total), 0);
}
@@ -642,4 +644,14 @@ abstract class Report
],
];
}
public function randHexColorPart(): string
{
return str_pad( dechex( mt_rand( 0, 255 ) ), 2, '0', STR_PAD_LEFT);
}
public function randHexColor(): string
{
return '#' . $this->randHexColorPart() . $this->randHexColorPart() . $this->randHexColorPart();
}
}

View File

@@ -283,8 +283,8 @@ abstract class Index extends Component
$items[] = [
'title' => ($key == 'overdue') ? trans('general.overdue') : trans('documents.statuses.' . $key),
//'href' => route($route, ['search' => 'status:' . $key]),
'amount' => money($total, default_currency(), true)->formatForHumans(),
'tooltip' => money($total, default_currency(), true)->format(),
'amount' => money($total)->formatForHumans(),
'tooltip' => money($total)->format(),
];
}

View File

@@ -3,6 +3,7 @@
namespace App\Abstracts\View\Components\Documents;
use App\Abstracts\View\Component;
use App\Interfaces\Utility\DocumentNumber;
use App\Models\Common\Contact;
use App\Models\Document\Document;
use App\Models\Setting\Currency;
@@ -163,30 +164,45 @@ abstract class Form extends Component
/** @var bool */
public $hideItemName;
/** @var bool */
public $hideSettingItemName;
/** @var string */
public $textItemName;
/** @var bool */
public $hideItemDescription;
/** @var bool */
public $hideSettingItemDescription;
/** @var string */
public $textItemDescription;
/** @var bool */
public $hideItemQuantity;
/** @var bool */
public $hideSettingItemQuantity;
/** @var string */
public $textItemQuantity;
/** @var bool */
public $hideItemPrice;
/** @var bool */
public $hideSettingItemPrice;
/** @var string */
public $textItemPrice;
/** @var bool */
public $hideItemAmount;
/** @var bool */
public $hideSettingItemAmount;
/** @var string */
public $textItemAmount;
@@ -276,8 +292,8 @@ abstract class Form extends Component
bool $hideDocumentTitle = false, bool $hideDocumentSubheading = false, string $title = '', string $subheading = '',
bool $hideIssuedAt = false, string $textIssuedAt = '', string $issuedAt = '', bool $hideDueAt = false, string $textDueAt = '', string $dueAt = '', $periodDueAt = '',
bool $hideDocumentNumber = false, string $textDocumentNumber = '', string $documentNumber = '', bool $hideOrderNumber = false, string $textOrderNumber = '', string $orderNumber = '',
bool $hideEditItemColumns = false, bool $hideItems = false, bool $hideItemName = false, string $textItemName = '', bool $hideItemDescription = false, string $textItemDescription = '',
bool $hideItemQuantity = false, string $textItemQuantity = '', bool $hideItemPrice = false, string $textItemPrice = '', bool $hideItemAmount = false, string $textItemAmount = '',
bool $hideEditItemColumns = false, bool $hideItems = false, bool $hideItemName = false, bool $hideSettingItemName = false, string $textItemName = '', bool $hideItemDescription = false, bool $hideSettingItemDescription = false, string $textItemDescription = '',
bool $hideItemQuantity = false, bool $hideSettingItemQuantity = false, string $textItemQuantity = '', bool $hideItemPrice = false, bool $hideSettingItemPrice = false, string $textItemPrice = '', bool $hideItemAmount = false, bool $hideSettingItemAmount = false, string $textItemAmount = '',
bool $hideDiscount = false, bool $isSalePrice = false, bool $isPurchasePrice = false, int $searchCharLimit = 0, string $notes = '',
bool $showRecurring = false,
bool $hideAdvanced = false, string $textSectionAdvancedTitle = '', string $textSectionAdvancedDescription = '',
@@ -290,9 +306,11 @@ abstract class Form extends Component
$this->model = ! empty($model) ? $model : $document;
$this->document = $this->model;
$this->currencies = $this->getCurrencies($currencies);
$this->currency = $this->getCurrency($document, $currency, $currency_code);
$this->currency_code = ! empty($this->currency) ? $this->currency->code : default_currency();
$this->currency = $this->getCurrency($document, $currency, $currency_code);
$this->currencies = $this->getCurrencies($currencies);
$this->taxes = Tax::enabled()->orderBy('name')->get()->pluck('title', 'id');
/* -- Content Start -- */
@@ -357,18 +375,23 @@ abstract class Form extends Component
$this->hideItems = $this->getHideItems($type, $hideItems, $hideItemName, $hideItemDescription);
$this->hideItemName = $this->getHideItemName($type, $hideItemName);
$this->hideSettingItemName = $this->getHideSettingItemName($type, $hideSettingItemName);
$this->textItemName = $this->getTextItemName($type, $textItemName);
$this->hideItemDescription = $this->getHideItemDescription($type, $hideItemDescription);
$this->hideSettingItemDescription = $this->getHideSettingItemDescription($type, $hideSettingItemDescription);
$this->textItemDescription = $this->getTextItemDescription($type, $textItemDescription);
$this->hideItemQuantity = $this->getHideItemQuantity($type, $hideItemQuantity);
$this->hideSettingItemQuantity = $this->getHideSettingItemQuantity($type, $hideSettingItemQuantity);
$this->textItemQuantity = $this->getTextItemQuantity($type, $textItemQuantity);
$this->hideItemPrice = $this->getHideItemPrice($type, $hideItemPrice);
$this->hideSettingItemPrice = $this->getHideSettingItemPrice($type, $hideSettingItemPrice);
$this->textItemPrice = $this->getTextItemPrice($type, $textItemPrice);
$this->hideItemAmount = $this->getHideItemAmount($type, $hideItemAmount);
$this->hideSettingItemAmount = $this->getHideSettingItemAmount($type, $hideSettingItemAmount);
$this->textItemAmount = $this->getTextItemAmount($type, $textItemAmount);
$this->hideDiscount = $this->getHideDiscount($type, $hideDiscount);
@@ -763,7 +786,7 @@ abstract class Form extends Component
$issuedAt = Date::now()->toDateString();
}
$addDays = setting($this->getSettingKey($type, 'payment_terms'), 0) ?: 0;
$addDays = setting($this->getDocumentSettingKey($type, 'payment_terms'), 0) ?: 0;
$dueAt = Date::parse($issuedAt)->addDays($addDays)->toDateString();
@@ -815,10 +838,14 @@ abstract class Form extends Component
return $document->document_number;
}
$document_number = $this->getNextDocumentNumber($type);
$contact = ($this->contact instanceof Contact) ? $this->contact : null;
$utility = app(DocumentNumber::class);
$document_number = $utility->getNextNumber($type, $contact);
if (empty($document_number)) {
$document_number = $this->getNextDocumentNumber(Document::INVOICE_TYPE);
$document_number = $utility->getNextNumber(Document::INVOICE_TYPE, $contact);
}
return $document_number;
@@ -869,25 +896,35 @@ abstract class Form extends Component
return $hideItems;
}
protected function getHideItemName($type, $hideItemName)
protected function getHideItemName($type, $hideItemName): bool
{
if (! empty($hideItemName)) {
return $hideItemName;
}
// if you use settting translation
if ($hideItemName = setting($this->getSettingKey($type, 'item_name'), false) && $hideItemName == 'hide') {
return $hideItemName;
}
$hide = $this->getHideFromConfig($type, 'name');
if ($hide) {
return $hide;
}
// @todo what return value invoice or always false??
return setting('invoice.item_name', $hideItemName) == 'hide' ? true : false;
return false;
}
protected function getHideSettingItemName($type, $hideSettingItemName): bool
{
if (! empty($hideSettingItemName)) {
return $hideSettingItemName;
}
$hideItemName = setting($this->getDocumentSettingKey($type, 'item_name'), false);
// if you use settting translation
if ($hideItemName === 'hide') {
return true;
}
return false;
}
protected function getTextItemName($type, $textItemName)
@@ -897,18 +934,18 @@ abstract class Form extends Component
}
// if you use settting translation
if (setting($this->getSettingKey($type, 'item_name'), 'items') === 'custom') {
if (empty($textItemName = setting($this->getSettingKey($type, 'item_name_input')))) {
if (setting($this->getDocumentSettingKey($type, 'item_name'), 'items') === 'custom') {
if (empty($textItemName = setting($this->getDocumentSettingKey($type, 'item_name_input')))) {
$textItemName = 'general.items';
}
return $textItemName;
}
if (setting($this->getSettingKey($type, 'item_name')) !== null
&& (trans(setting($this->getSettingKey($type, 'item_name'))) != setting($this->getSettingKey($type, 'item_name')))
if (setting($this->getDocumentSettingKey($type, 'item_name')) !== null
&& (trans(setting($this->getDocumentSettingKey($type, 'item_name'))) != setting($this->getDocumentSettingKey($type, 'item_name')))
) {
return setting($this->getSettingKey($type, 'item_name'));
return setting($this->getDocumentSettingKey($type, 'item_name'));
}
$translation = $this->getTextFromConfig($type, 'items');
@@ -920,25 +957,33 @@ abstract class Form extends Component
return 'general.items';
}
protected function getHideItemDescription($type, $hideItemDescription)
protected function getHideItemDescription($type, $hideItemDescription): bool
{
if (! empty($hideItemDescription)) {
return $hideItemDescription;
}
// if you use settting translation
if ($hideItemDescription = setting($this->getSettingKey($type, 'hide_item_description'), false) && $hideItemDescription == 'hide') {
return $hideItemDescription;
}
$hide = $this->getHideFromConfig($type, 'description');
if ($hide) {
return $hide;
}
// @todo what return value invoice or always false??
return setting('invoice.hide_item_description', $hideItemDescription);
return false;
}
protected function getHideSettingItemDescription($type, $hideSettingItemDescription): bool
{
if (! empty($hideSettingItemDescription)) {
return $hideSettingItemDescription;
}
// if you use settting translation
if (setting($this->getDocumentSettingKey($type, 'hide_item_description'), false)) {
return true;
}
return false;
}
protected function getTextItemDescription($type, $textItemDescription)
@@ -956,25 +1001,35 @@ abstract class Form extends Component
return 'general.description';
}
protected function getHideItemQuantity($type, $hideItemQuantity)
protected function getHideItemQuantity($type, $hideItemQuantity): bool
{
if (! empty($hideItemQuantity)) {
return $hideItemQuantity;
}
// if you use settting translation
if ($hideItemQuantity = setting($this->getSettingKey($type, 'hide_quantity'), false) && $hideItemQuantity == 'hide') {
return $hideItemQuantity;
}
$hide = $this->getHideFromConfig($type, 'quantity');
if ($hide) {
return $hide;
}
// @todo what return value invoice or always false??
return setting('invoice.quantity_name', $hideItemQuantity) == 'hide' ? true : false;
return false;
}
protected function getHideSettingItemQuantity($type, $hideSettingItemQuantity): bool
{
if (! empty($hideSettingItemQuantity)) {
return $hideSettingItemQuantity;
}
$hideItemQuantity = setting($this->getDocumentSettingKey($type, 'quantity_name'), false);
// if you use settting translation
if ($hideItemQuantity === 'hide') {
return true;
}
return false;
}
protected function getTextItemQuantity($type, $textItemQuantity)
@@ -984,18 +1039,18 @@ abstract class Form extends Component
}
// if you use settting translation
if (setting($this->getSettingKey($type, 'quantity_name'), 'quantity') === 'custom') {
if (empty($textItemQuantity = setting($this->getSettingKey($type, 'quantity_name_input')))) {
if (setting($this->getDocumentSettingKey($type, 'quantity_name'), 'quantity') === 'custom') {
if (empty($textItemQuantity = setting($this->getDocumentSettingKey($type, 'quantity_name_input')))) {
$textItemQuantity = 'invoices.quantity';
}
return $textItemQuantity;
}
if (setting($this->getSettingKey($type, 'quantity_name')) !== null
&& (trans(setting($this->getSettingKey($type, 'quantity_name'))) != setting($this->getSettingKey($type, 'quantity_name')))
if (setting($this->getDocumentSettingKey($type, 'quantity_name')) !== null
&& (trans(setting($this->getDocumentSettingKey($type, 'quantity_name'))) != setting($this->getDocumentSettingKey($type, 'quantity_name')))
) {
return setting($this->getSettingKey($type, 'quantity_name'));
return setting($this->getDocumentSettingKey($type, 'quantity_name'));
}
$translation = $this->getTextFromConfig($type, 'quantity');
@@ -1007,25 +1062,35 @@ abstract class Form extends Component
return 'invoices.quantity';
}
protected function getHideItemPrice($type, $hideItemPrice)
protected function getHideItemPrice($type, $hideItemPrice): bool
{
if (! empty($hideItemPrice)) {
return $hideItemPrice;
}
// if you use settting translation
if ($hideItemPrice = setting($this->getSettingKey($type, 'hide_price'), false) && $hideItemPrice == 'hide') {
return $hideItemPrice;
}
$hide = $this->getHideFromConfig($type, 'price');
if ($hide) {
return $hide;
}
// @todo what return value invoice or always false??
return setting('invoice.price_name', $hideItemPrice) == 'hide' ? true : false;
return false;
}
protected function getHideSettingItemPrice($type, $hideSettingItemPrice): bool
{
if (! empty($hideSettingItemPrice)) {
return $hideSettingItemPrice;
}
$hideItemPrice = setting($this->getDocumentSettingKey($type, 'price_name'), false);
// if you use settting translation
if ($hideItemPrice === 'hide') {
return true;
}
return false;
}
protected function getTextItemPrice($type, $textItemPrice)
@@ -1035,18 +1100,18 @@ abstract class Form extends Component
}
// if you use settting translation
if (setting($this->getSettingKey($type, 'price_name'), 'price') === 'custom') {
if (empty($textItemPrice = setting($this->getSettingKey($type, 'price_name_input')))) {
if (setting($this->getDocumentSettingKey($type, 'price_name'), 'price') === 'custom') {
if (empty($textItemPrice = setting($this->getDocumentSettingKey($type, 'price_name_input')))) {
$textItemPrice = 'invoices.price';
}
return $textItemPrice;
}
if (setting($this->getSettingKey($type, 'price_name')) !== null
&& (trans(setting($this->getSettingKey($type, 'price_name'))) != setting($this->getSettingKey($type, 'price_name')))
if (setting($this->getDocumentSettingKey($type, 'price_name')) !== null
&& (trans(setting($this->getDocumentSettingKey($type, 'price_name'))) != setting($this->getDocumentSettingKey($type, 'price_name')))
) {
return setting($this->getSettingKey($type, 'price_name'));
return setting($this->getDocumentSettingKey($type, 'price_name'));
}
$translation = $this->getTextFromConfig($type, 'price');
@@ -1058,25 +1123,33 @@ abstract class Form extends Component
return 'invoices.price';
}
protected function getHideItemAmount($type, $hideItemAmount)
protected function getHideItemAmount($type, $hideItemAmount): bool
{
if (! empty($hideItemAmount)) {
return $hideItemAmount;
}
// if you use settting translation
if ($hideAmount = setting($this->getSettingKey($type, 'hide_amount'), false)) {
return $hideAmount;
}
$hide = $this->getHideFromConfig($type, 'amount');
if ($hide) {
return $hide;
}
// @todo what return value invoice or always false??
return setting('invoice.hide_amount', $hideAmount);
return false;
}
protected function getHideSettingItemAmount($type, $hideSettingItemAmount): bool
{
if (! empty($hideSettingItemAmount)) {
return $hideSettingItemAmount;
}
// if you use settting translation
if (setting($this->getDocumentSettingKey($type, 'hide_amount'), false)) {
return true;
}
return false;
}
protected function getTextItemAmount($type, $textItemAmount)
@@ -1101,7 +1174,7 @@ abstract class Form extends Component
}
// if you use settting translation
if ($hideDiscount = setting($this->getSettingKey($type, 'hide_discount'), false)) {
if ($hideDiscount = setting($this->getDocumentSettingKey($type, 'hide_discount'), false)) {
return $hideDiscount;
}
@@ -1122,7 +1195,7 @@ abstract class Form extends Component
}
// if you use settting translation
if ($settingCharLimit = setting($this->getSettingKey($type, 'item_search_chart_limit'), false)) {
if ($settingCharLimit = setting($this->getDocumentSettingKey($type, 'item_search_chart_limit'), false)) {
return $settingCharLimit;
}
@@ -1146,7 +1219,7 @@ abstract class Form extends Component
return $this->document->notes;
}
return setting($this->getSettingKey($this->type, 'notes'));
return setting($this->getDocumentSettingKey($this->type, 'notes'));
}
protected function getTextSectionAdvancedTitle($type, $textSectionAdvancedTitle)
@@ -1177,7 +1250,7 @@ abstract class Form extends Component
return $this->document->title;
}
return setting($this->getSettingKey($type, 'title'));
return setting($this->getDocumentSettingKey($type, 'title'));
}
protected function getSubheadingValue($type, $subheading)
@@ -1190,7 +1263,7 @@ abstract class Form extends Component
return $this->document->subheading;
}
return setting($this->getSettingKey($type, 'subheading'));
return setting($this->getDocumentSettingKey($type, 'subheading'));
}
protected function getFooterValue($footer)
@@ -1203,7 +1276,7 @@ abstract class Form extends Component
return $this->document->footer;
}
return setting($this->getSettingKey($this->type, 'footer'));
return setting($this->getDocumentSettingKey($this->type, 'footer'));
}
protected function getTypeCategory($type, $typeCategory)

View File

@@ -392,8 +392,8 @@ abstract class Index extends Component
foreach ($totals as $key => $total) {
$title = ($key == 'overdue') ? trans('general.overdue') : trans('documents.statuses.' . $key);
$href = route($route, ['search' => 'status:' . $key]);
$amount = money($total, default_currency(), true)->formatForHumans();
$tooltip = money($total, default_currency(), true)->format();
$amount = money($total)->formatForHumans();
$tooltip = money($total)->format();
$items[] = [
'title' => $title,

View File

@@ -11,7 +11,6 @@ use App\Abstracts\View\Component;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\URL;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Intervention\Image\Exception\NotReadableException;
use Image;
@@ -742,9 +741,7 @@ abstract class Show extends Component
return $textRecurringType;
}
$default_key = config('type.' . static::OBJECT_TYPE . '.' . $type . '.translation.prefix');
$translation = $this->getTextFromConfig($type, 'recurring_tye', $default_key);
$translation = config('type.' . static::OBJECT_TYPE . '.' . $type . '.translation.tab_document');
if (! empty($translation)) {
return $translation;
@@ -910,7 +907,7 @@ abstract class Show extends Component
return $template;
}
$documentTemplate = setting($this->getSettingKey($type, 'template'), 'default');
$documentTemplate = setting($this->getDocumentSettingKey($type, 'template'), 'default');
return $documentTemplate;
}
@@ -928,7 +925,7 @@ abstract class Show extends Component
if (! empty($media)) {
$path = $media->getDiskPath();
if (Storage::missing($path)) {
if (! $media->fileExists()) {
return $logo;
}
} else {
@@ -941,7 +938,7 @@ abstract class Show extends Component
$height = setting('invoice.logo_size_height');
if ($media) {
$image->make(Storage::get($path))->resize($width, $height)->encode();
$image->make($media->contents())->resize($width, $height)->encode();
} else {
$image->make($path)->resize($width, $height)->encode();
}
@@ -975,21 +972,28 @@ abstract class Show extends Component
return $backgroundColor;
}
if ($background_color = config('type.' . static::OBJECT_TYPE . '.' . $type . '.color', false)) {
return $background_color;
// checking setting color
$key = $this->getDocumentSettingKey($type, 'color');
if (! empty(setting($key))) {
$backgroundColor = setting($key);
}
if (! empty($alias = config('type.' . static::OBJECT_TYPE . '.' . $type . '.alias'))) {
$type = $alias . '.' . str_replace('-', '_', $type);
// checking config color
if (empty($backgroundColor) && $background_color = config('type.document.' . $type . '.color', false)) {
$backgroundColor = $background_color;
}
$backgroundColor = setting($this->getSettingKey($type, 'color'), '#55588b');
// set default color
if (empty($backgroundColor)) {
$backgroundColor = '#55588b';
}
return $this->getHexCodeOfTailwindClass($backgroundColor);
}
protected function getTextDocumentTitle($type, $textDocumentTitle)
{
{
if (! empty($textDocumentTitle)) {
return $textDocumentTitle;
}
@@ -998,7 +1002,7 @@ abstract class Show extends Component
return $this->document->title;
}
$key = $this->getSettingKey($type, 'title');
$key = $this->getDocumentSettingKey($type, 'title');
if (! empty(setting($key))) {
return setting($key);
@@ -1023,7 +1027,7 @@ abstract class Show extends Component
return $this->document->subheading;
}
$key = $this->getSettingKey($type, 'subheading');
$key = $this->getDocumentSettingKey($type, 'subheading');
if (!empty(setting($key))) {
return setting($key);
@@ -1153,8 +1157,8 @@ abstract class Show extends Component
}
// if you use settting translation
if (setting($this->getSettingKey($type, 'item_name'), 'items') == 'custom') {
if (empty($textItems = setting($this->getSettingKey($type, 'item_name_input')))) {
if (setting($this->getDocumentSettingKey($type, 'item_name'), 'items') == 'custom') {
if (empty($textItems = setting($this->getDocumentSettingKey($type, 'item_name_input')))) {
$textItems = 'general.items';
}
@@ -1177,8 +1181,8 @@ abstract class Show extends Component
}
// if you use settting translation
if (setting($this->getSettingKey($type, 'quantity_name'), 'quantity') === 'custom') {
if (empty($textQuantity = setting($this->getSettingKey($type, 'quantity_name_input')))) {
if (setting($this->getDocumentSettingKey($type, 'quantity_name'), 'quantity') === 'custom') {
if (empty($textQuantity = setting($this->getDocumentSettingKey($type, 'quantity_name_input')))) {
$textQuantity = 'invoices.quantity';
}
@@ -1201,8 +1205,8 @@ abstract class Show extends Component
}
// if you use settting translation
if (setting($this->getSettingKey($type, 'price_name'), 'price') === 'custom') {
if (empty($textPrice = setting($this->getSettingKey($type, 'price_name_input')))) {
if (setting($this->getDocumentSettingKey($type, 'price_name'), 'price') === 'custom') {
if (empty($textPrice = setting($this->getDocumentSettingKey($type, 'price_name_input')))) {
$textPrice = 'invoices.price';
}
@@ -1256,9 +1260,11 @@ abstract class Show extends Component
return $hideName;
}
$hideName = setting($this->getDocumentSettingKey($type, 'item_name'), false);
// if you use settting translation
if ($hideName = setting($this->getSettingKey($type, 'item_name'), false) && $hideName == 'hide') {
return $hideName;
if ($hideName === 'hide') {
return true;
}
$hide = $this->getHideFromConfig($type, 'name');
@@ -1267,8 +1273,7 @@ abstract class Show extends Component
return $hide;
}
// @todo what return value invoice or always false??
return setting('invoice.item_name', $hideName) == 'hide' ? true : false;
return false;
}
protected function getHideDescription($type, $hideDescription)
@@ -1278,8 +1283,8 @@ abstract class Show extends Component
}
// if you use settting translation
if ($hideDescription = setting($this->getSettingKey($type, 'hide_item_description'), false)) {
return $hideDescription;
if (setting($this->getDocumentSettingKey($type, 'hide_item_description'), false)) {
return true;
}
$hide = $this->getHideFromConfig($type, 'description');
@@ -1288,8 +1293,7 @@ abstract class Show extends Component
return $hide;
}
// @todo what return value invoice or always false??
return setting('invoice.hide_item_description', $hideDescription);
return false;
}
protected function getHideQuantity($type, $hideQuantity)
@@ -1298,9 +1302,11 @@ abstract class Show extends Component
return $hideQuantity;
}
$hideQuantity = setting($this->getDocumentSettingKey($type, 'quantity_name'), false);
// if you use settting translation
if ($hideQuantity = setting($this->getSettingKey($type, 'hide_quantity'), false) && $hideQuantity == 'hide') {
return $hideQuantity;
if ($hideQuantity === 'hide') {
return true;
}
$hide = $this->getHideFromConfig($type, 'quantity');
@@ -1309,8 +1315,7 @@ abstract class Show extends Component
return $hide;
}
// @todo what return value invoice or always false??
return setting('invoice.quantity_name', $hideQuantity) == 'hide' ? true : false;
return false;
}
protected function getHidePrice($type, $hidePrice)
@@ -1319,9 +1324,11 @@ abstract class Show extends Component
return $hidePrice;
}
$hidePrice = setting($this->getDocumentSettingKey($type, 'price_name'), false);
// if you use settting translation
if ($hidePrice = setting($this->getSettingKey($type, 'hide_price'), false) && $hidePrice == 'hide') {
return $hidePrice;
if ($hidePrice === 'hide') {
return true;
}
$hide = $this->getHideFromConfig($type, 'price');
@@ -1330,8 +1337,7 @@ abstract class Show extends Component
return $hide;
}
// @todo what return value invoice or always false??
return setting('invoice.price_name', $hidePrice) == 'hide' ? true : false;
return false;
}
protected function getHideDiscount($type, $hideDiscount)
@@ -1341,7 +1347,7 @@ abstract class Show extends Component
}
// if you use settting translation
if ($hideDiscount = setting($this->getSettingKey($type, 'hide_discount'), false)) {
if ($hideDiscount = setting($this->getDocumentSettingKey($type, 'hide_discount'), false)) {
return $hideDiscount;
}
@@ -1362,8 +1368,8 @@ abstract class Show extends Component
}
// if you use settting translation
if ($hideAmount = setting($this->getSettingKey($type, 'hide_amount'), false)) {
return $hideAmount;
if (setting($this->getDocumentSettingKey($type, 'hide_amount'), false)) {
return true;
}
$hide = $this->getHideFromConfig($type, 'amount');
@@ -1372,7 +1378,6 @@ abstract class Show extends Component
return $hide;
}
// @todo what return value invoice or always false??
return setting('invoice.hide_amount', $hideAmount);
return false;
}
}

View File

@@ -10,7 +10,6 @@ use App\Traits\Tailwind;
use App\Traits\ViewComponents;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Intervention\Image\Exception\NotReadableException;
use Image;
@@ -210,7 +209,7 @@ abstract class Template extends Component
return $template;
}
$documentTemplate = setting($this->getSettingKey($type, 'template'), 'default');
$documentTemplate = setting($this->getDocumentSettingKey($type, 'template'), 'default');
return $documentTemplate;
}
@@ -228,7 +227,7 @@ abstract class Template extends Component
if (! empty($media)) {
$path = $media->getDiskPath();
if (Storage::missing($path)) {
if (! $media->fileExists()) {
return $logo;
}
} else {
@@ -241,7 +240,7 @@ abstract class Template extends Component
$height = setting('invoice.logo_size_height');
if ($media) {
$image->make(Storage::get($path))->resize($width, $height)->encode();
$image->make($media->contents())->resize($width, $height)->encode();
} else {
$image->make($path)->resize($width, $height)->encode();
}
@@ -275,16 +274,22 @@ abstract class Template extends Component
return $backgroundColor;
}
if ($background_color = config('type.document.' . $type . '.color', false)) {
return $background_color;
// checking setting color
$key = $this->getDocumentSettingKey($type, 'color');
if (! empty(setting($key))) {
$backgroundColor = setting($key);
}
if (! empty($alias = config('type.document.' . $type . '.alias'))) {
$type = $alias . '.' . str_replace('-', '_', $type);
// checking config color
if (empty($backgroundColor) && $background_color = config('type.document.' . $type . '.color', false)) {
$backgroundColor = $background_color;
}
$backgroundColor = setting($this->getSettingKey($type, 'color'), '#55588b');
// set default color
if (empty($backgroundColor)) {
$backgroundColor = '#55588b';
}
return $this->getHexCodeOfTailwindClass($backgroundColor);
}
@@ -299,7 +304,7 @@ abstract class Template extends Component
return $this->document->title;
}
$key = $this->getSettingKey($type, 'title');
$key = $this->getDocumentSettingKey($type, 'title');
if (! empty(setting($key))) {
return setting($key);
@@ -324,7 +329,7 @@ abstract class Template extends Component
return $this->document->subheading;
}
$key = $this->getSettingKey($type, 'subheading');
$key = $this->getDocumentSettingKey($type, 'subheading');
if (! empty(setting($key))) {
return setting($key);
@@ -454,18 +459,18 @@ abstract class Template extends Component
}
// if you use settting translation
if (setting($this->getSettingKey($type, 'item_name'), 'items') === 'custom') {
if (empty($textItems = setting($this->getSettingKey($type, 'item_name_input')))) {
if (setting($this->getDocumentSettingKey($type, 'item_name'), 'items') === 'custom') {
if (empty($textItems = setting($this->getDocumentSettingKey($type, 'item_name_input')))) {
$textItems = 'general.items';
}
return $textItems;
}
if (setting($this->getSettingKey($type, 'item_name')) !== null
&& (trans(setting($this->getSettingKey($type, 'item_name'))) != setting($this->getSettingKey($type, 'item_name')))
if (setting($this->getDocumentSettingKey($type, 'item_name')) !== null
&& (trans(setting($this->getDocumentSettingKey($type, 'item_name'))) != setting($this->getDocumentSettingKey($type, 'item_name')))
) {
return setting($this->getSettingKey($type, 'item_name'));
return setting($this->getDocumentSettingKey($type, 'item_name'));
}
$translation = $this->getTextFromConfig($type, 'items');
@@ -484,18 +489,18 @@ abstract class Template extends Component
}
// if you use settting translation
if (setting($this->getSettingKey($type, 'quantity_name'), 'quantity') === 'custom') {
if (empty($textQuantity = setting($this->getSettingKey($type, 'quantity_name_input')))) {
if (setting($this->getDocumentSettingKey($type, 'quantity_name'), 'quantity') === 'custom') {
if (empty($textQuantity = setting($this->getDocumentSettingKey($type, 'quantity_name_input')))) {
$textQuantity = 'invoices.quantity';
}
return $textQuantity;
}
if (setting($this->getSettingKey($type, 'quantity_name')) !== null
&& (trans(setting($this->getSettingKey($type, 'quantity_name'))) != setting($this->getSettingKey($type, 'quantity_name')))
if (setting($this->getDocumentSettingKey($type, 'quantity_name')) !== null
&& (trans(setting($this->getDocumentSettingKey($type, 'quantity_name'))) != setting($this->getDocumentSettingKey($type, 'quantity_name')))
) {
return setting($this->getSettingKey($type, 'quantity_name'));
return setting($this->getDocumentSettingKey($type, 'quantity_name'));
}
$translation = $this->getTextFromConfig($type, 'quantity');
@@ -514,18 +519,18 @@ abstract class Template extends Component
}
// if you use settting translation
if (setting($this->getSettingKey($type, 'price_name'), 'price') === 'custom') {
if (empty($textPrice = setting($this->getSettingKey($type, 'price_name_input')))) {
if (setting($this->getDocumentSettingKey($type, 'price_name'), 'price') === 'custom') {
if (empty($textPrice = setting($this->getDocumentSettingKey($type, 'price_name_input')))) {
$textPrice = 'invoices.price';
}
return $textPrice;
}
if (setting($this->getSettingKey($type, 'price_name')) !== null
&& (trans(setting($this->getSettingKey($type, 'price_name'))) != setting($this->getSettingKey($type, 'price_name')))
if (setting($this->getDocumentSettingKey($type, 'price_name')) !== null
&& (trans(setting($this->getDocumentSettingKey($type, 'price_name'))) != setting($this->getDocumentSettingKey($type, 'price_name')))
) {
return setting($this->getSettingKey($type, 'price_name'));
return setting($this->getDocumentSettingKey($type, 'price_name'));
}
$translation = $this->getTextFromConfig($type, 'price');
@@ -592,9 +597,11 @@ abstract class Template extends Component
return $hideName;
}
$hideName = setting($this->getDocumentSettingKey($type, 'item_name'), false);
// if you use settting translation
if ($hideName = setting($this->getSettingKey($type, 'item_name'), false) && $hideName == 'hide') {
return $hideName;
if ($hideName === 'hide') {
return true;
}
$hide = $this->getHideFromConfig($type, 'name');
@@ -603,8 +610,7 @@ abstract class Template extends Component
return $hide;
}
// @todo what return value invoice or always false??
return setting('invoice.item_name', $hideName) == 'hide' ? true : false;
return false;
}
protected function getHideDescription($type, $hideDescription)
@@ -614,8 +620,8 @@ abstract class Template extends Component
}
// if you use settting translation
if ($hideDescription = setting($this->getSettingKey($type, 'hide_item_description'), false)) {
return $hideDescription;
if (setting($this->getDocumentSettingKey($type, 'hide_item_description'), false)) {
return true;
}
$hide = $this->getHideFromConfig($type, 'description');
@@ -624,8 +630,7 @@ abstract class Template extends Component
return $hide;
}
// @todo what return value invoice or always false??
return setting('invoice.hide_item_description', $hideDescription);
return false;
}
protected function getHideQuantity($type, $hideQuantity)
@@ -634,9 +639,11 @@ abstract class Template extends Component
return $hideQuantity;
}
$hideQuantity = setting($this->getDocumentSettingKey($type, 'quantity_name'), false);
// if you use settting translation
if ($hideQuantity = setting($this->getSettingKey($type, 'hide_quantity'), false) && $hideQuantity == 'hide') {
return $hideQuantity;
if ($hideQuantity === 'hide') {
return true;
}
$hide = $this->getHideFromConfig($type, 'quantity');
@@ -645,8 +652,7 @@ abstract class Template extends Component
return $hide;
}
// @todo what return value invoice or always false??
return setting('invoice.quantity_name', $hideQuantity) == 'hide' ? true : false;
return false;
}
protected function getHidePrice($type, $hidePrice)
@@ -655,9 +661,11 @@ abstract class Template extends Component
return $hidePrice;
}
$hidePrice = setting($this->getDocumentSettingKey($type, 'price_name'), false);
// if you use settting translation
if ($hidePrice = setting($this->getSettingKey($type, 'hide_price'), false) && $hidePrice == 'hide') {
return $hidePrice;
if ($hidePrice === 'hide') {
return true;
}
$hide = $this->getHideFromConfig($type, 'price');
@@ -666,8 +674,7 @@ abstract class Template extends Component
return $hide;
}
// @todo what return value invoice or always false??
return setting('invoice.price_name', $hidePrice) == 'hide' ? true : false;
return false;
}
protected function getHideDiscount($type, $hideDiscount)
@@ -677,7 +684,7 @@ abstract class Template extends Component
}
// if you use settting translation
if ($hideDiscount = setting($this->getSettingKey($type, 'hide_discount'), false)) {
if ($hideDiscount = setting($this->getDocumentSettingKey($type, 'hide_discount'), false)) {
return $hideDiscount;
}
@@ -698,8 +705,8 @@ abstract class Template extends Component
}
// if you use settting translation
if ($hideAmount = setting($this->getSettingKey($type, 'hide_amount'), false)) {
return $hideAmount;
if (setting($this->getDocumentSettingKey($type, 'hide_amount'), false)) {
return true;
}
$hide = $this->getHideFromConfig($type, 'amount');
@@ -708,8 +715,7 @@ abstract class Template extends Component
return $hide;
}
// @todo what return value invoice or always false??
return setting('invoice.hide_amount', $hideAmount);
return false;
}
protected function getPrint($print)

View File

@@ -9,7 +9,6 @@ use App\Traits\Transactions;
use App\Utilities\Modules;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\URL;
use Illuminate\Support\Str;
use Intervention\Image\Facades\Image;
@@ -483,7 +482,7 @@ abstract class Show extends Component
return $template;
}
$transactionTemplate = setting($this->getSettingKey($type, 'template')) ?: 'default';
$transactionTemplate = setting($this->getTransactionSettingKey($type, 'template')) ?: 'default';
return $transactionTemplate;
}
@@ -501,7 +500,7 @@ abstract class Show extends Component
if (! empty($media)) {
$path = $media->getDiskPath();
if (Storage::missing($path)) {
if (! $media->fileExists()) {
return $logo;
}
} else {
@@ -514,7 +513,7 @@ abstract class Show extends Component
$height = setting('invoice.logo_size_height');
if ($media) {
$image->make(Storage::get($path))->resize($width, $height)->encode();
$image->make($media->contents())->resize($width, $height)->encode();
} else {
$image->make($path)->resize($width, $height)->encode();
}
@@ -1114,9 +1113,7 @@ abstract class Show extends Component
return $textRecurringType;
}
$default_key = config('type.transaction.' . $type . '.translation.transactions');
$translation = $this->getTextFromConfig($type, 'recurring_type', $default_key);
$translation = config('type.transaction.' . $type . '.translation.transactions');
if (! empty($translation)) {
return $translation;

View File

@@ -9,7 +9,6 @@ use App\Traits\Transactions;
use App\Utilities\Modules;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Intervention\Image\Facades\Image;
use Intervention\Image\Exception\NotReadableException;
@@ -277,7 +276,7 @@ abstract class Template extends Component
if (!empty($media)) {
$path = $media->getDiskPath();
if (Storage::missing($path)) {
if (! $media->fileExists()) {
return $logo;
}
} else {
@@ -290,7 +289,7 @@ abstract class Template extends Component
$height = setting('invoice.logo_size_height');
if ($media) {
$image->make(Storage::get($path))->resize($width, $height)->encode();
$image->make($media->contents())->resize($width, $height)->encode();
} else {
$image->make($path)->resize($width, $height)->encode();
}

View File

@@ -31,7 +31,7 @@ class Bills extends BulkAction
],
'cancelled' => [
'icon' => 'cancel',
'name' => 'general.cancel',
'name' => 'documents.actions.cancel',
'message' => 'bulk_actions.message.cancelled',
'permission' => 'update-purchases-bills',
],
@@ -67,7 +67,7 @@ class Bills extends BulkAction
$bills = $this->getSelectedRecords($request);
foreach ($bills as $bill) {
if ($bill->status == 'cancelled') {
if (in_array($bill->status, ['cancelled', 'draft'])) {
continue;
}

View File

@@ -31,7 +31,7 @@ class Invoices extends BulkAction
],
'cancelled' => [
'icon' => 'cancel',
'name' => 'general.cancel',
'name' => 'documents.actions.cancel',
'message' => 'bulk_actions.message.cancelled',
'permission' => 'update-sales-invoices',
],
@@ -67,7 +67,7 @@ class Invoices extends BulkAction
$invoices = $this->getSelectedRecords($request);
foreach ($invoices as $invoice) {
if ($invoice->status == 'cancelled') {
if (in_array($invoice->status, ['cancelled', 'draft'])) {
continue;
}

View File

@@ -1,29 +0,0 @@
<?php
namespace App\Classifiers;
use Wnx\LaravelStats\ReflectionClass;
use Wnx\LaravelStats\Contracts\Classifier;
class Transformer implements Classifier
{
public function name(): string
{
return 'Transformers';
}
public function satisfies(ReflectionClass $class): bool
{
return $class->isSubclassOf(\League\Fractal\TransformerAbstract::class);
}
public function countsTowardsApplicationCode(): bool
{
return true;
}
public function countsTowardsTests(): bool
{
return false;
}
}

View File

@@ -125,6 +125,6 @@ class DownloadModule extends Command
$version = Versions::getLatestVersion($url, $current);
}
return $version;
return $version?->latest;
}
}

View File

@@ -71,12 +71,18 @@ class RecurringCheck extends Command
$company_name = !empty($recur->company->name) ? $recur->company->name : 'Missing Company Name : ' . $recur->company->id;
$template = $recur->recurable()->where('company_id', $recur->company_id)->first();
// Check if company is disabled
if (! $recur->company->enabled) {
$this->info($company_name . ' company is disabled. Skipping...');
if (Date::parse($recur->company->updated_at)->format('Y-m-d') > Date::now()->subMonth(3)->format('Y-m-d')) {
$recur->delete();
if ($template) {
$template->delete();
}
}
continue;
@@ -98,12 +104,16 @@ class RecurringCheck extends Command
$recur->delete();
if ($template) {
$template->delete();
}
continue;
}
company($recur->company_id)->makeCurrent();
if (! $template = $recur->recurable) {
if (! $template) {
$this->info('Missing model.');
$recur->delete();

View File

@@ -68,6 +68,14 @@ class Update extends Command
$this->old = $this->getOldVersion();
if (version_compare($this->old, $this->new, '>=')) {
$message = 'The current version for the ' . $this->alias . ' is the latest version!';
$this->info($message);
return self::CMD_SUCCESS;
}
company($this->company)->makeCurrent();
if (!$path = $this->download()) {
@@ -91,7 +99,7 @@ class Update extends Command
public function getNewVersion()
{
return ($this->argument('new') == 'latest') ? Versions::latest($this->alias) : $this->argument('new');
return ($this->argument('new') == 'latest') ? Versions::latest($this->alias)?->latest : $this->argument('new');
}
public function getOldVersion()

View File

@@ -0,0 +1,23 @@
<?php
namespace App\Events\Common;
use App\Abstracts\Event;
class DatesFormating extends Event
{
public $columns;
public $request;
/**
* Create a new event instance.
*
* @param $request
*/
public function __construct($columns, $request)
{
$this->columns = $columns;
$this->request = $request;
}
}

View File

@@ -0,0 +1,20 @@
<?php
namespace App\Events\Document;
use App\Abstracts\Event;
class DocumentDeleted extends Event
{
public $document;
/**
* Create a new event instance.
*
* @param $document
*/
public function __construct($document)
{
$this->document = $document;
}
}

View File

@@ -0,0 +1,20 @@
<?php
namespace App\Events\Document;
use App\Abstracts\Event;
class DocumentDeleting extends Event
{
public $document;
/**
* Create a new event instance.
*
* @param $document
*/
public function __construct($document)
{
$this->document = $document;
}
}

View File

@@ -0,0 +1,20 @@
<?php
namespace App\Events\Document;
use App\Abstracts\Event;
class DocumentSending extends Event
{
public $document;
/**
* Create a new event instance.
*
* @param $document
*/
public function __construct($document)
{
$this->document = $document;
}
}

View File

@@ -0,0 +1,51 @@
<?php
namespace App\Events\Email;
use App\Abstracts\Event;
use App\Models\Auth\User;
use App\Models\Common\Contact;
class InvalidEmailDetected extends Event
{
public $email;
public $error;
public $contact = null;
public $user = null;
public function __construct(string $email, string $error)
{
$this->email = $email;
$this->error = $error;
$this->setContact();
$this->setUser();
}
public function setContact()
{
$contact = Contact::email($this->email)->enabled()->first();
if (empty($contact)) {
return;
}
$this->contact = $contact;
}
public function setUser()
{
$user = User::email($this->email)->enabled()->first();
if (empty($user)) {
return;
}
$this->user = $user;
}
}

View File

@@ -0,0 +1,15 @@
<?php
namespace App\Events\Email;
use App\Abstracts\Event;
class TooManyEmailsSent extends Event
{
public $user_id;
public function __construct(int $user_id)
{
$this->user_id = $user_id;
}
}

View File

@@ -3,6 +3,7 @@
namespace App\Exceptions;
use Akaunting\Money\Exceptions\UnexpectedAmountException;
use App\Events\Email\InvalidEmailDetected;
use App\Exceptions\Http\Resource as ResourceException;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Database\Eloquent\ModelNotFoundException;
@@ -15,6 +16,7 @@ use Illuminate\View\ViewException;
use Symfony\Component\Debug\Exception\FatalThrowableError;
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Mailer\Exception\HttpTransportException as MailerHttpTransportException;
use Throwable;
class Handler extends ExceptionHandler
@@ -72,6 +74,14 @@ class Handler extends ExceptionHandler
*/
public function report(Throwable $exception)
{
if ($exception instanceof MailerHttpTransportException) {
$email = $this->handleMailerExceptions($exception);
if (! empty($email)) {
return;
}
}
parent::report($exception);
}
@@ -194,6 +204,24 @@ class Handler extends ExceptionHandler
}
}
if ($exception instanceof MailerHttpTransportException) {
$email = $this->handleMailerExceptions($exception);
if (! empty($email)) {
$message = trans('notifications.menu.invalid_email.description', ['email' => $email]);
if ($request->ajax()) {
return response()->json([
'error' => $message,
], $exception->getCode());
}
return response()->view('errors.403', [
'message' => $message,
], $exception->getCode());
}
}
return parent::render($request, $exception);
}
@@ -214,6 +242,28 @@ class Handler extends ExceptionHandler
return new Response($response, $this->getStatusCode($exception), $this->getHeaders($exception));
}
protected function handleMailerExceptions(MailerHttpTransportException $exception): string
{
/**
* Couldn't access the SentMessage object to get the email address
* https://symfony.com/doc/current/mailer.html#debugging-emails
*
* https://codespeedy.com/extract-email-addresses-from-a-string-in-php
* https://phpliveregex.com/p/IMG
*/
preg_match("/[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}/", $exception->getMessage(), $matches);
if (empty($matches[0])) {
return '';
}
$email = $matches[0];
event(new InvalidEmailDetected($email, $exception->getMessage()));
return $email;
}
/**
* Prepare the replacements array by gathering the keys and values.
*

View File

@@ -22,6 +22,8 @@ class BillItems extends Export
$model->bill_number = $document->document_number;
$model->item_name = $model->item->name;
$model->item_description = $model->item->description;
$model->item_type = $model->item->type;
return parent::map($model);
}
@@ -31,6 +33,8 @@ class BillItems extends Export
return [
'bill_number',
'item_name',
'item_description',
'item_type',
'quantity',
'price',
'total',

View File

@@ -22,6 +22,8 @@ class Customers extends Export
$model->country = $country;
$model->can_login = $model->user_id ? true : false;
return parent::map($model);
}
@@ -41,6 +43,7 @@ class Customers extends Export
'currency_code',
'reference',
'enabled',
'can_login',
];
}
}

View File

@@ -22,6 +22,8 @@ class InvoiceItems extends Export
$model->invoice_number = $document->document_number;
$model->item_name = $model->item->name;
$model->item_description = $model->item->description;
$model->item_type = $model->item->type;
return parent::map($model);
}
@@ -31,6 +33,8 @@ class InvoiceItems extends Export
return [
'invoice_number',
'item_name',
'item_description',
'item_type',
'quantity',
'price',
'total',

View File

@@ -5,7 +5,7 @@ namespace App\Http\Controllers\Auth;
use App\Abstracts\Http\Controller;
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
use Illuminate\Http\Request;
use App\Http\Requests\Auth\Forgot as Request;
use Illuminate\Support\Facades\Password;
class Forgot extends Controller
@@ -42,13 +42,11 @@ class Forgot extends Controller
/**
* Send a reset link to the given user.
*
* @param \Illuminate\Http\Request $request
* @param \App\Http\Requests\Auth\Forgot $request
* @return \Illuminate\Http\RedirectResponse
*/
public function store(Request $request)
{
$this->validateEmail($request);
// We will send the password reset link to this user. Once we have attempted
// to send the link, we will examine the response then see the message we
// need to show to the user. Finally, we'll send out a proper response.
@@ -96,7 +94,7 @@ class Forgot extends Controller
'error' => true,
'message' => trans('passwords.user'),
'data' => null,
'redirect' => null,
'redirect' => route('forgot'),
];
return response()->json($response);

View File

@@ -4,7 +4,8 @@ namespace App\Http\Controllers\Auth;
use App\Abstracts\Http\Controller;
use Illuminate\Foundation\Auth\ResetsPasswords;
use Illuminate\Http\Request;
use Illuminate\Http\Request as BaseRequest;
use App\Http\Requests\Auth\Reset as Request;
use Illuminate\Support\Facades\Password;
use Illuminate\Support\Str;
@@ -29,17 +30,22 @@ class Reset extends Controller
$this->middleware('guest');
}
public function create(Request $request, $token = null)
public function create(BaseRequest $request, $token = null)
{
return view('auth.reset.create')->with(
['token' => $token, 'email' => $request->email]
);
}
/**
* Send a reset link to the given user.
*
* @param \App\Http\Requests\Auth\Reset $request
*
* @return \Illuminate\Http\JsonResponse
*/
public function store(Request $request)
{
$this->validate($request, $this->rules(), $this->validationErrorMessages());
// Here we will attempt to reset the user's password. If it is successful we
// will update the password on an actual user model and persist it to the
// database. Otherwise we will parse the error and return the response.

View File

@@ -47,9 +47,19 @@ class Users extends Controller
*
* @return Response
*/
public function show()
public function show(User $user)
{
return redirect()->route('users.index');
$u = new \stdClass();
$u->role = $user->roles()->first();
$u->landing_pages = [];
event(new LandingPageShowing($u));
$landing_pages = $u->landing_pages;
$companies = $user->companies()->collect();
return view('auth.users.show', compact('user', 'landing_pages', 'companies'));
}
/**
@@ -67,7 +77,13 @@ class Users extends Controller
$landing_pages = $u->landing_pages;
$roles = Role::all()->reject(function ($r) {
return $r->hasPermission('read-client-portal');
$status = $r->hasPermission('read-client-portal');
if ($r->name == 'employee') {
$status = true;
}
return $status;
})->pluck('display_name', 'id');
$companies = user()->companies()->take(setting('default.select_limit'))->get()->sortBy('name')->pluck('name', 'id');
@@ -89,7 +105,7 @@ class Users extends Controller
$response = $this->ajaxDispatch(new CreateUser($request));
if ($response['success']) {
$response['redirect'] = route('users.index');
$response['redirect'] = route('users.show', $response['data']->id);
$message = trans('messages.success.invited', ['type' => trans_choice('general.users', 1)]);
@@ -129,12 +145,21 @@ class Users extends Controller
if ($user->isCustomer()) {
// Show only roles with customer permission
$roles = Role::all()->reject(function ($r) {
return !$r->hasPermission('read-client-portal');
return ! $r->hasPermission('read-client-portal');
})->pluck('display_name', 'id');
} else if ($user->isEmployee()) {
// Show only roles with employee permission
$roles = Role::where('name', 'employee')->get()->pluck('display_name', 'id');
} else {
// Don't show roles with customer permission
$roles = Role::all()->reject(function ($r) {
return $r->hasPermission('read-client-portal');
$status = $r->hasPermission('read-client-portal');
if ($r->name == 'employee') {
$status = true;
}
return $status;
})->pluck('display_name', 'id');
}
@@ -176,7 +201,7 @@ class Users extends Controller
$response = $this->ajaxDispatch(new UpdateUser($user, $request));
if ($response['success']) {
$response['redirect'] = user()->can('read-auth-users') ? route('users.index') : route('users.edit', $user->id);
$response['redirect'] = user()->can('read-auth-users') ? route('users.show', $user->id) : route('users.edit', $user->id);
$message = trans('messages.success.updated', ['type' => $user->name]);
@@ -341,8 +366,6 @@ class Users extends Controller
{
$response = $this->ajaxDispatch(new CreateInvitation($user, company()));
$response['redirect'] = route('users.index');
if ($response['success']) {
$message = trans('messages.success.invited', ['type' => trans_choice('general.users', 1)]);
@@ -353,7 +376,7 @@ class Users extends Controller
flash($message)->error()->important();
}
return response()->json($response);
return redirect()->route('users.index');
}
/**

View File

@@ -35,16 +35,16 @@ class Accounts extends Controller
*/
public function show(Account $account)
{
$transactions = Transaction::with('category', 'contact', 'document')->where('account_id', $account->id)->collect(['paid_at'=> 'desc']);
$transactions = Transaction::with('category', 'contact', 'contact.media', 'document', 'document.totals', 'document.media', 'recurring', 'media')->where('account_id', $account->id)->collect(['paid_at'=> 'desc']);
$transfers = Transfer::with('expense_transaction', 'expense_transaction.account', 'income_transaction', 'income_transaction.account')
->whereHas('expense_transaction', fn ($query) => $query->where('account_id', $account->id))
->orWhereHas('income_transaction', fn ($query) => $query->where('account_id', $account->id))
->collect(['expense_transaction.paid_at' => 'desc']);
$incoming_amount = money($account->income_balance, $account->currency_code, true);
$outgoing_amount = money($account->expense_balance, $account->currency_code, true);
$current_amount = money($account->balance, $account->currency_code, true);
$incoming_amount = money($account->income_balance, $account->currency_code);
$outgoing_amount = money($account->expense_balance, $account->currency_code);
$current_amount = money($account->balance, $account->currency_code);
$summary_amounts = [
'incoming_exact' => $incoming_amount->format(),

View File

@@ -24,8 +24,8 @@ class Reconciliations extends Controller
{
$reconciliations = Reconciliation::with('account')->collect();
$reconciled_amount = money($reconciliations->where('reconciled', 1)->sum('closing_balance'), default_currency(), true);
$in_progress_amount = money($reconciliations->where('reconciled', 0)->sum('closing_balance'), default_currency(), true);
$reconciled_amount = money($reconciliations->where('reconciled', 1)->sum('closing_balance'));
$in_progress_amount = money($reconciliations->where('reconciled', 0)->sum('closing_balance'));
$summary_amounts = [
'amount_exact' => $reconciled_amount->format(),
@@ -246,9 +246,9 @@ class Reconciliations extends Controller
$difference = $closing_balance - $cleared_amount;
$json->closing_balance = money($closing_balance, $currency_code, true)->format();
$json->cleared_amount = money($cleared_amount, $currency_code, true)->format();
$json->difference = money($difference, $currency_code, true)->format();
$json->closing_balance = money($closing_balance, $currency_code)->format();
$json->cleared_amount = money($cleared_amount, $currency_code)->format();
$json->difference = money($difference, $currency_code)->format();
$json->difference_raw = (int) $difference;
return response()->json($json);

View File

@@ -63,9 +63,9 @@ class RecurringTransactions extends Controller
*/
public function create()
{
$type = request()->get('type', 'income-recurring');
$real_type = request()->get('real_type', $this->getRealTypeOfRecurringTransaction($type));
$contact_type = config('type.transaction.' . $real_type . '.contact_type');
$type = $this->getTypeRecurringTransaction(request()->get('type', 'income-recurring'));
$real_type = $this->getTypeTransaction(request()->get('real_type', $this->getRealTypeOfRecurringTransaction($type)));
$contact_type = config('type.transaction.' . $real_type . '.contact_type', 'customer');
$number = $this->getNextTransactionNumber('-recurring');
@@ -139,8 +139,8 @@ class RecurringTransactions extends Controller
public function edit(Transaction $recurring_transaction)
{
$type = $recurring_transaction->type;
$real_type = request()->get('real_type', $this->getRealTypeOfRecurringTransaction($type));
$contact_type = config('type.transaction.' . $real_type . '.contact_type');
$real_type = $this->getTypeTransaction(request()->get('real_type', $this->getRealTypeOfRecurringTransaction($type)));
$contact_type = config('type.transaction.' . $real_type . '.contact_type', 'customer');
$number = $this->getNextTransactionNumber('-recurring');

View File

@@ -56,9 +56,9 @@ class Transactions extends Controller
$totals['profit'] = $totals['income'] - $totals['expense'];
$incoming_amount = money($totals['income'], default_currency(), true);
$expense_amount = money($totals['expense'], default_currency(), true);
$profit_amount = money($totals['profit'], default_currency(), true);
$incoming_amount = money($totals['income']);
$expense_amount = money($totals['expense']);
$profit_amount = money($totals['profit']);
$summary_amounts = [
'incoming_exact' => $incoming_amount->format(),
@@ -98,10 +98,10 @@ class Transactions extends Controller
*/
public function create()
{
$type = request()->get('type', 'income');
$type = $this->getTypeTransaction(request()->get('type', 'income'));
$real_type = $this->getRealTypeTransaction($type);
$number = $this->getNextTransactionNumber();
$number = $this->getNextTransactionNumber($type);
$contact_type = config('type.transaction.' . $type . '.contact_type');

View File

@@ -61,19 +61,28 @@ class BulkActions extends Controller
$result = $bulk_actions->{$handle}($request);
$message = trans($bulk_actions->messages['general'], ['type' => $handle, 'count' => count($request->get('selected'))]);
$count = count($request->get('selected'));
$not_passed = 0;
if (array_key_exists($handle, $bulk_actions->messages)) {
flash()->messages->each(function ($message) use (&$not_passed) {
if (in_array($message->level, ['danger', 'warning'])) {
$not_passed++;
}
});
$message = trans($bulk_actions->messages['general'], ['type' => $handle, 'count' => $count - $not_passed]);
if (array_key_exists($handle, $bulk_actions->messages) && $not_passed === 0) {
$message = trans($bulk_actions->messages[$handle], ['type' => $page]);
}
if (! empty($result) && ($result instanceof \Symfony\Component\HttpFoundation\BinaryFileResponse)) {
flash($message)->success();
$level = $not_passed > 0 ? 'info' : 'success';
flash($message)->{$level}();
if (! empty($result) && ($result instanceof \Symfony\Component\HttpFoundation\BinaryFileResponse)) {
return $result;
} elseif (! empty($result) && ($result instanceof RedirectResponse)) {
flash($message)->success();
return response()->json([
'success' => true,
'redirect' => $result->getTargetUrl(),
@@ -82,8 +91,6 @@ class BulkActions extends Controller
'message' => ''
]);
} else {
flash($message)->success();
return response()->json([
'success' => true,
'redirect' => true,

View File

@@ -16,6 +16,15 @@ class Companies extends Controller
{
use Uploads, Users;
public function __construct()
{
// Add CRUD permission checks to all methods only remove index method for all companies list.
$this->middleware('permission:create-common-companies')->only('create', 'store', 'duplicate', 'import');
$this->middleware('permission:read-common-companies')->only('show', 'edit', 'export');
$this->middleware('permission:update-common-companies')->only('update', 'enable', 'disable');
$this->middleware('permission:delete-common-companies')->only('destroy');
}
/**
* Display a listing of the resource.
*

View File

@@ -34,6 +34,22 @@ class Uploads extends Controller
return $this->streamMedia($media);
}
public function inline($id)
{
try {
$media = Media::find($id);
} catch (\Exception $e) {
return response(null, 204);
}
// Get file path
if (!$this->getMediaPathOnStorage($media)) {
return response(null, 204);
}
return $this->streamMedia($media, 'inline');
}
/**
* Get the specified resource.
*

View File

@@ -15,7 +15,12 @@ class Database extends Controller
*/
public function create()
{
return view('install.database.create');
return view('install.database.create', [
'host' => env('DB_HOST' , 'localhost'),
'username' => env('DB_USERNAME', ''),
'password' => env('DB_PASSWORD', ''),
'database' => env('DB_DATABASE', ''),
]);
}
/**
@@ -34,9 +39,10 @@ class Database extends Controller
$database = $request['database'];
$username = $request['username'];
$password = $request['password'];
$prefix = config("database.connections.$connection.prefix", null);
// Check database connection
if (!Installer::createDbTables($host, $port, $database, $username, $password)) {
if (!Installer::createDbTables($host, $port, $database, $username, $password, $prefix)) {
$response = [
'status' => null,
'success' => false,

View File

@@ -14,7 +14,15 @@ class Language extends Controller
*/
public function create()
{
return view('install.language.create');
$locale = config('app.locale');
$lang_allowed = language()->allowed();
if (! $locale || ! array_key_exists($locale, $lang_allowed)) {
$locale = 'en-GB';
}
return view('install.language.create', compact('locale', 'lang_allowed'));
}
/**

View File

@@ -29,11 +29,13 @@ class Settings extends Controller
public function store(Request $request)
{
DB::transaction(function () use ($request) {
$locale = session('locale') ?? config('app.locale');
// Create company
Installer::createCompany($request->get('company_name'), $request->get('company_email'), session('locale'));
Installer::createCompany($request->get('company_name'), $request->get('company_email'), $locale);
// Create user
Installer::createUser($request->get('user_email'), $request->get('user_password'), session('locale'));
Installer::createUser($request->get('user_email'), $request->get('user_password'), $locale);
});
// Make the final touches

View File

@@ -48,7 +48,9 @@ class Updates extends Controller
$m->name = $row->getName();
$m->alias = $row->get('alias');
$m->installed = $row->get('version');
$m->latest = $updates[$alias];
$m->latest = $updates[$alias]->latest;
$m->errors = $updates[$alias]->errors;
$m->message = $updates[$alias]->message;
$modules[] = $m;
}

View File

@@ -64,7 +64,7 @@ class Currencies extends Controller
*/
public function store(Request $request)
{
$currency = config('money.' . $request->get('code'));
$currency = config('money.currencies.' . $request->get('code'));
$request['precision'] = (int) $currency['precision'];
$request['symbol'] = $currency['symbol'];

View File

@@ -32,22 +32,22 @@ class DocumentItemColumns extends Controller
$item_names = [
'hide' => trans('settings.invoice.hide.item_name'),
'settings.invoice.item' => trans('settings.' . $type . '.item'),
'settings.invoice.product' => trans('settings.' . $type . '.product'),
'settings.invoice.service' => trans('settings.' . $type . '.service'),
'settings.invoice.item' => trans('settings.invoice.item'),
'settings.invoice.product' => trans('settings.invoice.product'),
'settings.invoice.service' => trans('settings.invoice.service'),
'custom' => trans('settings.invoice.custom'),
];
$price_names = [
'hide' => trans('settings.invoice.hide.price'),
'settings.invoice.price' => trans('settings.' . $type . '.price'),
'settings.invoice.rate' => trans('settings.' . $type . '.rate'),
'settings.invoice.price' => trans('settings.invoice.price'),
'settings.invoice.rate' => trans('settings.invoice.rate'),
'custom' => trans('settings.invoice.custom'),
];
$quantity_names = [
'hide' => trans('settings.invoice.hide.quantity'),
'settings.invoice.quantity' => trans('settings.' . $type . '.quantity'),
'settings.invoice.quantity' => trans('settings.invoice.quantity'),
'custom' => trans('settings.invoice.custom'),
];
@@ -60,17 +60,17 @@ class DocumentItemColumns extends Controller
'90' => trans('settings.invoice.due_days', ['days' => 90]),
];
$item_name = setting($this->getSettingKey($type, 'item_name'));
$item_name_input = setting($this->getSettingKey($type, 'item_name_input'));
$price_name = setting($this->getSettingKey($type, 'price_name'));
$price_name_input = setting($this->getSettingKey($type, 'price_name_input'));
$quantity_name = setting($this->getSettingKey($type, 'quantity_name'));
$quantity_name_input = setting($this->getSettingKey($type, 'quantity_name_input'));
$hide_item_name = setting($this->getSettingKey($type, 'hide_item_name'));
$hide_item_description = setting($this->getSettingKey($type, 'hide_item_description'));
$hide_quantity = setting($this->getSettingKey($type, 'hide_quantity'));
$hide_price = setting($this->getSettingKey($type, 'hide_price'));
$hide_amount = setting($this->getSettingKey($type, 'hide_amount'));
$item_name = setting($this->getDocumentSettingKey($type, 'item_name'));
$item_name_input = setting($this->getDocumentSettingKey($type, 'item_name_input'));
$price_name = setting($this->getDocumentSettingKey($type, 'price_name'));
$price_name_input = setting($this->getDocumentSettingKey($type, 'price_name_input'));
$quantity_name = setting($this->getDocumentSettingKey($type, 'quantity_name'));
$quantity_name_input = setting($this->getDocumentSettingKey($type, 'quantity_name_input'));
$hide_item_name = setting($this->getDocumentSettingKey($type, 'hide_item_name'));
$hide_item_description = setting($this->getDocumentSettingKey($type, 'hide_item_description'));
$hide_quantity = setting($this->getDocumentSettingKey($type, 'hide_quantity'));
$hide_price = setting($this->getDocumentSettingKey($type, 'hide_price'));
$hide_amount = setting($this->getDocumentSettingKey($type, 'hide_amount'));
$html = view('modals.documents.item_columns', compact(
'type',
@@ -117,7 +117,7 @@ class DocumentItemColumns extends Controller
}
foreach ($fields as $key => $value) {
$real_key = $this->getSettingKey($type, $key);
$real_key = $this->getDocumentSettingKey($type, $key);
// Don't process unwanted keys
if (in_array($key, $this->skip_keys)) {

View File

@@ -51,9 +51,9 @@ class DocumentTransactions extends Controller
$document->{$document_total->code} = $document_total->amount;
}
$total = money($document->total, $currency->code, true)->format();
$total = money($document->total, $currency->code)->format();
$document->grand_total = money($total, $currency->code)->getAmount();
$document->grand_total = money($total, $currency->code, false)->getAmount();
if (! empty($paid)) {
$document->grand_total = round($document->total - $paid, $currency->precision);
@@ -140,7 +140,7 @@ class DocumentTransactions extends Controller
$number = $transaction->number;
$document->grand_total = money($transaction->amount, $currency->code)->getAmount();
$document->grand_total = money($transaction->amount, $currency->code, false)->getAmount();
$document->paid_at = $transaction->paid_at;

View File

@@ -242,16 +242,30 @@ class Item extends Controller
$this->dispatch(new InstallModule($request['alias'], company_id()));
$name = module($request['alias'])->getName();
$module_routes = module_attribute($request['alias'], 'routes', []);
$message = trans('modules.installed', ['module' => $name]);
flash($message)->success();
$redirect = route('apps.app.show', $request['alias']);
// Get module.json redirect route
if (! empty($module_routes['redirect_after_install'])) {
if (is_array($module_routes['redirect_after_install'])) {
$route = array_shift($module_routes['redirect_after_install']);
$redirect = route($route, $module_routes['redirect_after_install']);
} else {
$redirect = route($module_routes['redirect_after_install']);
}
}
$json = [
'success' => true,
'error' => false,
'message' => null,
'redirect' => route('apps.app.show', $request['alias']),
'redirect' => $redirect,
'data' => [
'name' => $name,
'alias' => $request['alias'],

View File

@@ -194,20 +194,19 @@ class Tiles extends Controller
case 'paid':
$response = $this->getPaidModules($data);
$last_page = $response->last_page;
$last_page = ! empty($response) ? $response->last_page : 1;
$modules = $this->prepareModules($response);
break;
case 'new':
$response = $this->getNewModules($data);
$last_page = $response->last_page;
$last_page = ! empty($response) ? $response->last_page : 1;
$modules = $this->prepareModules($response);
break;
case 'free':
$response = $this->getFreeModules($data);
$last_page = $response->last_page;
$last_page = ! empty($response) ? $response->last_page : 1;
$modules = $this->prepareModules($response);
break;
case 'search':
@@ -215,7 +214,7 @@ class Tiles extends Controller
$response = $this->getSearchModules($data);
$last_page = $response->last_page;
$last_page = ! empty($response) ? $response->last_page : 1;
$modules = $this->prepareModules($response);
break;
}

View File

@@ -7,7 +7,6 @@ use App\Exports\Purchases\Bills as Export;
use App\Http\Requests\Common\Import as ImportRequest;
use App\Http\Requests\Document\Document as Request;
use App\Imports\Purchases\Bills as Import;
use App\Jobs\Banking\CreateBankingDocumentTransaction;
use App\Jobs\Document\CreateDocument;
use App\Jobs\Document\DeleteDocument;
use App\Jobs\Document\DuplicateDocument;
@@ -31,7 +30,7 @@ class Bills extends Controller
*/
public function index()
{
$bills = Document::bill()->with('contact', 'transactions')->collect(['issued_at' => 'desc']);
$bills = Document::bill()->with('contact', 'items', 'item_taxes', 'last_history', 'transactions', 'totals', 'histories', 'media')->collect(['issued_at' => 'desc']);
return $this->response('purchases.bills.index', compact('bills'));
}

View File

@@ -30,7 +30,7 @@ class Vendors extends Controller
*/
public function index()
{
$vendors = Contact::with('bills.transactions')->vendor()->collect();
$vendors = Contact::with('bills.histories', 'bills.totals', 'bills.transactions', 'media')->vendor()->collect();
return $this->response('purchases.vendors.index', compact('vendors'));
}
@@ -152,7 +152,7 @@ class Vendors extends Controller
$response = $this->ajaxDispatch(new UpdateContact($vendor, $request));
if ($response['success']) {
$response['redirect'] = route('vendors.index');
$response['redirect'] = route('vendors.show', $response['data']->id);
$message = trans('messages.success.updated', ['type' => $vendor->name]);

View File

@@ -30,7 +30,7 @@ class Customers extends Controller
*/
public function index()
{
$customers = Contact::customer()->with('invoices.transactions')->collect();
$customers = Contact::customer()->with('invoices.histories', 'invoices.totals', 'invoices.transactions', 'media')->collect();
return $this->response('sales.customers.index', compact('customers'));
}
@@ -152,7 +152,7 @@ class Customers extends Controller
$response = $this->ajaxDispatch(new UpdateContact($customer, $request));
if ($response['success']) {
$response['redirect'] = route('customers.index');
$response['redirect'] = route('customers.show', $response['data']->id);
$message = trans('messages.success.updated', ['type' => $customer->name]);

View File

@@ -10,9 +10,9 @@ use App\Imports\Sales\Invoices as Import;
use App\Jobs\Document\CreateDocument;
use App\Jobs\Document\DeleteDocument;
use App\Jobs\Document\DuplicateDocument;
use App\Jobs\Document\SendDocument;
use App\Jobs\Document\UpdateDocument;
use App\Models\Document\Document;
use App\Notifications\Sale\Invoice as Notification;
use App\Traits\Documents;
class Invoices extends Controller
@@ -31,7 +31,7 @@ class Invoices extends Controller
*/
public function index()
{
$invoices = Document::invoice()->with('contact', 'transactions')->collect(['document_number'=> 'desc']);
$invoices = Document::invoice()->with('contact', 'items', 'item_taxes', 'last_history', 'transactions', 'totals', 'histories', 'media')->collect(['document_number'=> 'desc']);
return $this->response('sales.invoices.index', compact('invoices'));
}
@@ -260,12 +260,17 @@ class Invoices extends Controller
return redirect()->back();
}
// Notify the customer
$invoice->contact->notify(new Notification($invoice, 'invoice_new_customer', true));
$response = $this->ajaxDispatch(new SendDocument($invoice));
event(new \App\Events\Document\DocumentSent($invoice));
if ($response['success']) {
$message = trans('documents.messages.email_sent', ['type' => trans_choice('general.invoices', 1)]);
flash(trans('documents.messages.email_sent', ['type' => trans_choice('general.invoices', 1)]))->success();
flash($message)->success();
} else {
$message = $response['message'];
flash($message)->error()->important();
}
return redirect()->back();
}

View File

@@ -30,10 +30,10 @@ class Categories extends Controller
$query->withSubcategory();
}
$categories = $query->collect();
$types = $this->getCategoryTypes();
$categories = $query->type(array_keys($types))->collect();
return $this->response('settings.categories.index', compact('categories', 'types'));
}
@@ -173,7 +173,9 @@ class Categories extends Controller
];
});
return view('settings.categories.edit', compact('category', 'types', 'type_disabled', 'categories'));
$parent_categories = $categories[$category->type] ?? [];
return view('settings.categories.edit', compact('category', 'types', 'type_disabled', 'categories', 'parent_categories'));
}
/**

View File

@@ -232,13 +232,13 @@ class Currencies extends Controller
$code = request('code');
$currencies = Currency::all()->pluck('rate', 'code');
if ($code) {
$currency = config('money.' . $code);
$currencies = Currency::all()->pluck('rate', 'code');
$currency = config('money.currencies.' . $code);
$currency['rate'] = isset($currencies[$code]) ? $currencies[$code] : null;
$currency['symbol_first'] = $currency['symbol_first'] ? 1 : 0;
$currency['symbol_first'] = ! empty($currency['symbol_first']) ? 1 : 0;
$json = (object) $currency;
}

View File

@@ -3,6 +3,7 @@
namespace App\Http\Controllers\Settings;
use App\Abstracts\Http\SettingController;
use App\Models\Banking\Account;
use App\Models\Setting\Category;
use App\Models\Setting\Tax;
@@ -10,6 +11,8 @@ class Defaults extends SettingController
{
public function edit()
{
$accounts = Account::enabled()->orderBy('name')->get()->pluck('title', 'id');
$sales_categories = Category::income()->enabled()->orderBy('name')->take(setting('default.select_limit'))->get();
$sale_category_id = setting('default.income_category');
@@ -37,6 +40,7 @@ class Defaults extends SettingController
$taxes = Tax::enabled()->orderBy('name')->get()->pluck('title', 'id');
return view('settings.default.edit', compact(
'accounts',
'sales_categories',
'purchases_categories',
'taxes',

View File

@@ -16,7 +16,7 @@ class Kernel extends HttpKernel
protected $middleware = [
\App\Http\Middleware\TrustHosts::class,
\App\Http\Middleware\TrustProxies::class,
\Fruitcake\Cors\HandleCors::class,
\Illuminate\Http\Middleware\HandleCors::class,
\Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
\App\Http\Middleware\TrimStrings::class,
@@ -141,13 +141,13 @@ class Kernel extends HttpKernel
];
/**
* The application's route middleware.
* The application's middleware aliases.
*
* These middleware may be assigned to groups or used individually.
* Aliases may be used to conveniently assign middleware to routes and groups.
*
* @var array
* @var array<string, class-string|string>
*/
protected $routeMiddleware = [
protected $middlewareAliases = [
// Laravel
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,

View File

@@ -26,11 +26,11 @@ class Browser extends Component
$this->status = false;
}
} elseif (Str::contains($user_agent, 'Edg')) {
// $view = 'livewire.notification.browser.chrome';
} elseif (Str::contains($user_agent, 'Safari')) {
// $view = 'livewire.notification.browser.edge';
} elseif (Str::contains($user_agent, 'Chrome')) {
} elseif (Str::contains($user_agent, 'Safari')) {
// $view = 'livewire.notification.browser.safari';
} elseif (Str::contains($user_agent, 'Chrome')) {
// $view = 'livewire.notification.browser.chrome';
} elseif (Str::contains($user_agent, 'Opera')) {
// $view = 'livewire.notification.browser.opera';
}

View File

@@ -2,6 +2,7 @@
namespace App\Http\Middleware;
use App\Events\Common\DatesFormating;
use Closure;
use Date;
@@ -10,14 +11,29 @@ class DateFormat
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @param \Illuminate\Http\Request $request
* @param \Closure $next
*
* @return mixed
*/
public function handle($request, Closure $next)
{
if (($request->method() == 'POST') || ($request->method() == 'PATCH')) {
$fields = ['paid_at', 'due_at', 'issued_at', 'started_at', 'ended_at', 'expire_at'];
$columns = new \stdClass();
$columns->fields = [
'paid_at',
'due_at',
'issued_at',
'started_at',
'ended_at',
'expire_at',
'recurring_started_at',
'recurring_limit_date',
];
event(new DatesFormating($columns, $request));
$fields = $columns->fields;
foreach ($fields as $field) {
$date = $request->get($field);
@@ -27,7 +43,7 @@ class DateFormat
}
if (Date::parse($date)->format('H:i:s') == '00:00:00') {
$new_date = Date::parse($date)->format('Y-m-d') . ' ' . Date::now()->format('H:i:s');
$new_date = Date::parse($date)->format('Y-m-d') . ' ' . Date::now()->format('H:i:s');
} else {
$new_date = Date::parse($date)->toDateTimeString();
}

View File

@@ -84,7 +84,7 @@ class Money
$amount = $item['price'];
if (strpos($item['price'], config('money.' . $currency_code . '.symbol')) !== false) {
if (strpos($item['price'], config('money.currencies.' . $currency_code . '.symbol')) !== false) {
$amount = $this->getAmount($item['price'], $currency_code);
}
@@ -101,11 +101,11 @@ class Money
protected function getAmount($money_format, $currency_code)
{
try {
if (config('money.' . $currency_code . '.decimal_mark') !== '.') {
$money_format = Str::replaceFirst('.', config('money.' . $currency_code . '.decimal_mark'), $money_format);
if (config('money.currencies.' . $currency_code . '.decimal_mark') !== '.') {
$money_format = Str::replaceFirst('.', config('money.currencies.' . $currency_code . '.decimal_mark'), $money_format);
}
$amount = money($money_format, $currency_code)->getAmount();
$amount = money($money_format, $currency_code, false)->getAmount();
} catch (InvalidArgumentException | OutOfBoundsException | UnexpectedValueException $e) {
report($e);

View File

@@ -0,0 +1,30 @@
<?php
namespace App\Http\Requests\Auth;
use Illuminate\Foundation\Http\FormRequest;
class Forgot extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'email' => 'required|email',
];
}
}

View File

@@ -0,0 +1,32 @@
<?php
namespace App\Http\Requests\Auth;
use Illuminate\Foundation\Http\FormRequest;
class Reset extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'token' => 'required',
'email' => 'required|email',
'password' => 'required|confirmed|min:6',
];
}
}

View File

@@ -29,7 +29,7 @@ class Transaction extends FormRequest
}
// Get company id
$company_id = (int) $this->request->get('company_id');
$company_id = (int) $this->request->get('company_id', company_id());
$attachment = 'nullable';
@@ -37,7 +37,7 @@ class Transaction extends FormRequest
$attachment = 'mimes:' . config('filesystems.mimes') . '|between:0,' . config('filesystems.max_size') * 1024;
}
return [
$rules = [
'type' => 'required|string',
'number' => 'required|string|unique:transactions,NULL,' . $id . ',id,company_id,' . $company_id . ',deleted_at,NULL',
'account_id' => 'required|integer',
@@ -53,6 +53,29 @@ class Transaction extends FormRequest
'recurring_count' => 'gte:0',
'recurring_interval' => 'exclude_unless:recurring_frequency,custom|gt:0',
];
// Is Recurring
if ($this->request->has('recurring_frequency')) {
// first line of the recurring rule
if ($this->request->get('recurring_frequency') == 'custom') {
$rules['recurring_interval'] = 'required|gte:1';
$rules['recurring_custom_frequency'] = 'required|string|in:daily,weekly,monthly,yearly';
}
// second line of the recurring rule
$rules['recurring_started_at'] = 'required|date_format:Y-m-d H:i:s';
switch($this->request->get('recurring_limit')) {
case 'date':
$rules['recurring_limit_date'] = 'required|date_format:Y-m-d H:i:s|after_or_equal:recurring_started_at';
break;
case 'count':
$rules['recurring_limit_count'] = 'required|gte:0';
break;
}
}
return $rules;
}
public function withValidator($validator)
@@ -61,6 +84,18 @@ class Transaction extends FormRequest
$paid_at = Date::parse($this->request->get('paid_at'))->format('Y-m-d');
$this->request->set('paid_at', $paid_at);
if ($this->request->get('recurring_started_at')) {
$recurring_started_at = Date::parse($this->request->get('recurring_started_at'))->format('Y-m-d');
$this->request->set('recurring_started_at', $recurring_started_at);
}
if ($this->request->get('recurring_limit_date')) {
$recurring_limit_date = Date::parse($this->request->get('recurring_limit_date'))->format('Y-m-d');
$this->request->set('recurring_limit_date', $recurring_limit_date);
}
}
}
}

View File

@@ -14,9 +14,10 @@ class CustomMail extends FormRequest
public function rules()
{
return [
'to' => 'required|email',
'subject' => 'required|string',
'body' => 'required|string',
'to' => 'required|email',
'subject' => 'required|string',
'body' => 'required|string',
'attachments.*' => 'nullable|boolean',
];
}
}

View File

@@ -31,7 +31,7 @@ class Item extends FormRequest
}
return [
'type' => 'required|string',
'type' => 'required|string|in:product,service',
'name' => 'required|string',
'sale_price' => $sale_price . '|regex:/^(?=.*?[0-9])[0-9.,]+$/',
'purchase_price' => $purchase_price . '|regex:/^(?=.*?[0-9])[0-9.,]+$/',

View File

@@ -48,6 +48,7 @@ class Document extends FormRequest
$rules = [
'type' => 'required|string',
'document_number' => 'required|string|unique:documents,NULL,' . $id . ',id,type,' . $type . ',company_id,' . $company_id . ',deleted_at,NULL',
//'status' => 'required|string|in:draft,paid,partial,sent,received,viewed,cancelled',
'status' => 'required|string',
'issued_at' => 'required|date_format:Y-m-d H:i:s|before_or_equal:due_at',
'due_at' => 'required|date_format:Y-m-d H:i:s|after_or_equal:issued_at',
@@ -65,6 +66,27 @@ class Document extends FormRequest
'recurring_interval' => 'exclude_unless:recurring_frequency,custom|gt:0',
];
// Is Recurring
if ($this->request->has('recurring_frequency')) {
// first line of the recurring rule
if ($this->request->get('recurring_frequency') == 'custom') {
$rules['recurring_interval'] = 'required|gte:1';
$rules['recurring_custom_frequency'] = 'required|string|in:daily,weekly,monthly,yearly';
}
// second line of the recurring rule
$rules['recurring_started_at'] = 'required|date_format:Y-m-d H:i:s';
switch($this->request->get('recurring_limit')) {
case 'date':
$rules['recurring_limit_date'] = 'required|date_format:Y-m-d H:i:s|after_or_equal:recurring_started_at';
break;
case 'count':
$rules['recurring_limit_count'] = 'required|gte:0';
break;
}
}
$items = $this->request->all('items');
if ($items) {
@@ -93,6 +115,18 @@ class Document extends FormRequest
$this->request->set('issued_at', $issued_at);
$this->request->set('due_at', $due_at);
if ($this->request->has('recurring_started_at')) {
$recurring_started_at = Date::parse($this->request->get('recurring_started_at'))->format('Y-m-d');
$this->request->set('recurring_started_at', $recurring_started_at);
}
if ($this->request->has('recurring_limit_date')) {
$recurring_limit_date = Date::parse($this->request->get('recurring_limit_date'))->format('Y-m-d');
$this->request->set('recurring_limit_date', $recurring_limit_date);
}
}
}

View File

@@ -13,9 +13,11 @@ class Category extends FormRequest
*/
public function rules()
{
$types = collect(config('type.category'))->keys();
return [
'name' => 'required|string',
'type' => 'required|string',
'type' => 'required|string|in:' . $types->implode(','),
'color' => 'required|string',
];
}

View File

@@ -45,6 +45,10 @@ class Setting extends FormRequest
$rules['number_digit'] = 'required|integer|min:1|max:20';
}
if ($this->request->has('number_next')) {
$rules['number_next'] = 'required|integer';
}
return $rules;
}

View File

@@ -22,9 +22,9 @@ class Tax extends FormRequest
$enabled = 'nullable';
}
$company_id = (int) $this->request->get('company_id');
$company_id = (int) $this->request->get('company_id', company_id());
$type = 'required|string';
$type = 'required|string|in:fixed,normal,inclusive,withholding,compound';
if (!empty($this->request->get('type')) && $this->request->get('type') == 'compound') {
$type .= '|unique:taxes,NULL,' . $id . ',id,company_id,' . $company_id . ',type,compound,deleted_at,NULL';
@@ -32,7 +32,7 @@ class Tax extends FormRequest
return [
'name' => 'required|string',
'rate' => 'required|min:0|max:100',
'rate' => 'required|numeric|min:0|max:100',
'type' => $type,
'enabled' => $enabled,
];

View File

@@ -22,9 +22,9 @@ class Account extends JsonResource
'number' => $this->number,
'currency_code' => $this->currency_code,
'opening_balance' => $this->opening_balance,
'opening_balance_formatted' => money($this->opening_balance, $this->currency_code, true)->format(),
'opening_balance_formatted' => money($this->opening_balance, $this->currency_code)->format(),
'current_balance' => $this->balance,
'current_balance_formatted' => money($this->balance, $this->currency_code, true)->format(),
'current_balance_formatted' => money($this->balance, $this->currency_code)->format(),
'bank_name' => $this->bank_name,
'bank_phone' => $this->bank_phone,
'bank_address' => $this->bank_address,

View File

@@ -22,7 +22,7 @@ class Reconciliation extends JsonResource
'started_at' => $this->started_at->toIso8601String(),
'ended_at' => $this->ended_at->toIso8601String(),
'closing_balance' => $this->closing_balance,
'closing_balance_formatted' => money($this->closing_balance, default_currency(), true)->format(),
'closing_balance_formatted' => money($this->closing_balance)->format(),
'reconciled' => $this->reconciled,
'created_from' => $this->created_from,
'created_by' => $this->created_by,

View File

@@ -25,7 +25,7 @@ class Transaction extends JsonResource
'account_id' => $this->account_id,
'paid_at' => $this->paid_at->toIso8601String(),
'amount' => $this->amount,
'amount_formatted' => money($this->amount, $this->currency_code, true)->format(),
'amount_formatted' => money($this->amount, $this->currency_code)->format(),
'currency_code' => $this->currency_code,
'currency_rate' => $this->currency_rate,
'document_id' => $this->document_id,

View File

@@ -25,7 +25,7 @@ class Transfer extends JsonResource
'to_account' => $income_transaction->account->name,
'to_account_id' => $income_transaction->account->id,
'amount' => $expense_transaction->amount,
'amount_formatted' => money($expense_transaction->amount, $expense_transaction->currency_code, true)->format(),
'amount_formatted' => money($expense_transaction->amount, $expense_transaction->currency_code)->format(),
'currency_code' => $expense_transaction->currency_code,
'paid_at' => $expense_transaction->paid_at ? $expense_transaction->paid_at->toIso8601String() : '',
'created_from' => $this->created_from,

View File

@@ -23,9 +23,9 @@ class Item extends JsonResource
'name' => $this->name,
'description' => $this->description,
'sale_price' => $this->sale_price,
'sale_price_formatted' => money($this->sale_price, default_currency(), true)->format(),
'sale_price_formatted' => money((double) $this->sale_price)->format(),
'purchase_price' => $this->purchase_price,
'purchase_price_formatted' => money($this->purchase_price, default_currency(), true)->format(),
'purchase_price_formatted' => money((double) $this->purchase_price)->format(),
'category_id' => $this->category_id,
'picture' => $this->picture,
'enabled' => $this->enabled,

View File

@@ -8,6 +8,7 @@ use App\Http\Resources\Document\DocumentHistory;
use App\Http\Resources\Document\DocumentItem;
use App\Http\Resources\Document\DocumentItemTax;
use App\Http\Resources\Document\DocumentTotal;
use App\Http\Resources\Setting\Category;
use App\Http\Resources\Setting\Currency;
use Illuminate\Http\Resources\Json\JsonResource;
@@ -31,7 +32,8 @@ class Document extends JsonResource
'issued_at' => $this->issued_at ? $this->issued_at->toIso8601String() : '',
'due_at' => $this->due_at ? $this->due_at->toIso8601String() : '',
'amount' => $this->amount,
'amount_formatted' => money($this->amount, $this->currency_code, true)->format(),
'amount_formatted' => money($this->amount, $this->currency_code)->format(),
'category_id' => $this->category_id,
'currency_code' => $this->currency_code,
'currency_rate' => $this->currency_rate,
'contact_id' => $this->contact_id,
@@ -50,6 +52,7 @@ class Document extends JsonResource
'created_by' => $this->created_by,
'created_at' => $this->created_at ? $this->created_at->toIso8601String() : '',
'updated_at' => $this->updated_at ? $this->updated_at->toIso8601String() : '',
'category' => new Category($this->category),
'currency' => new Currency($this->currency),
'contact' => new Contact($this->contact),
'histories' => [static::$wrap => DocumentHistory::collection($this->histories)],

View File

@@ -22,10 +22,11 @@ class DocumentItem extends JsonResource
'document_id' => $this->document_id,
'item_id' => $this->item_id,
'name' => $this->name,
'description' => $this->description,
'price' => $this->price,
'price_formatted' => money($this->price, $this->document->currency_code, true)->format(),
'price_formatted' => money($this->price, $this->document->currency_code)->format(),
'total' => $this->total,
'total_formatted' => money($this->total, $this->document->currency_code, true)->format(),
'total_formatted' => money($this->total, $this->document->currency_code)->format(),
'created_from' => $this->created_from,
'created_by' => $this->created_by,
'created_at' => $this->created_at ? $this->created_at->toIso8601String() : '',

View File

@@ -24,7 +24,7 @@ class DocumentItemTax extends JsonResource
'tax_id' => $this->tax_id,
'name' => $this->name,
'amount' => $this->amount,
'amount_formatted' => money($this->amount, $this->document->currency_code, true)->format(),
'amount_formatted' => money($this->amount, $this->document->currency_code)->format(),
'created_from' => $this->created_from,
'created_by' => $this->created_by,
'created_at' => $this->created_at ? $this->created_at->toIso8601String() : '',

View File

@@ -22,7 +22,7 @@ class DocumentTotal extends JsonResource
'code' => $this->code,
'name' => $this->name,
'amount' => $this->amount,
'amount_formatted' => money($this->amount, $this->document->currency_code, true)->format(),
'amount_formatted' => money($this->amount, $this->document->currency_code)->format(),
'sort_order' => $this->sort_order,
'created_from' => $this->created_from,
'created_by' => $this->created_by,

View File

@@ -8,6 +8,8 @@ use App\Models\Banking\Transaction as Model;
class Transactions extends Import
{
public $request_class = Request::class;
public function model(array $row)
{
return new Model($row);
@@ -17,17 +19,12 @@ class Transactions extends Import
{
$row = parent::map($row);
$row['currency_code'] = $this->getCurrencyCode($row);
$row['account_id'] = $this->getAccountId($row);
$row['category_id'] = $this->getCategoryId($row);
$row['contact_id'] = $this->getContactId($row);
$row['currency_code'] = $this->getCurrencyCode($row);
$row['document_id'] = $this->getDocumentId($row);
return $row;
}
public function rules(): array
{
return (new Request())->rules();
}
}

View File

@@ -26,10 +26,10 @@ class Transfers extends Import
$row = parent::map($row);
$row['transferred_at'] = Date::parse($row['transferred_at'])->format('Y-m-d');
$row['from_account_id'] = $this->getFromAccountId($row);
$row['to_account_id'] = $this->getToAccountId($row);
$row['from_currency_code'] = $this->getFromCurrencyCode($row);
$row['to_currency_code'] = $this->getToCurrencyCode($row);
$row['from_account_id'] = $this->getFromAccountId($row);
$row['to_account_id'] = $this->getToAccountId($row);
$row['expense_transaction_id'] = $this->getExpenseTransactionId($row);
$row['income_transaction_id'] = $this->getIncomeTransactionId($row);
@@ -41,10 +41,10 @@ class Transfers extends Import
return [
'from_account_id' => 'required|integer',
'from_currency_code' => 'required|string|currency',
'from_currency_rate' => 'required',
'from_currency_rate' => 'required|gt:0',
'to_account_id' => 'required|integer',
'to_currency_code' => 'required|string|currency',
'to_currency_rate' => 'required',
'to_currency_rate' => 'required|gt:0',
'amount' => 'required|amount',
'transferred_at' => 'required|date_format:Y-m-d',
'payment_method' => 'required|string',

View File

@@ -4,11 +4,12 @@ namespace App\Imports\Common\Sheets;
use App\Abstracts\Import;
use App\Http\Requests\Common\ItemTax as Request;
use App\Models\Common\Item;
use App\Models\Common\ItemTax as Model;
class ItemTaxes extends Import
{
public $request_class = Request::class;
public function model(array $row)
{
return new Model($row);
@@ -16,21 +17,16 @@ class ItemTaxes extends Import
public function map($row): array
{
$row = parent::map($row);
$row['item_id'] = (int) Item::where('name', $row['item_name'])->value('id');
if ($this->isEmpty($row, 'item_id')) {
if ($this->isEmpty($row, 'item_name')) {
return [];
}
$row = parent::map($row);
$row['item_id'] = $this->getItemId($row);
$row['tax_id'] = $this->getTaxId($row);
return $row;
}
public function rules(): array
{
return (new Request())->rules();
}
}

View File

@@ -8,6 +8,8 @@ use App\Models\Common\Item as Model;
class Items extends Import
{
public $request_class = Request::class;
public function model(array $row)
{
return new Model($row);
@@ -25,9 +27,4 @@ class Items extends Import
return $row;
}
public function rules(): array
{
return (new Request())->rules();
}
}

View File

@@ -9,6 +9,8 @@ use App\Models\Document\DocumentHistory as Model;
class BillHistories extends Import
{
public $request_class = Request::class;
public function model(array $row)
{
return new Model($row);
@@ -20,6 +22,8 @@ class BillHistories extends Import
return [];
}
$row['bill_number'] = (string) $row['bill_number'];
$row = parent::map($row);
$row['document_id'] = (int) Document::bill()->number($row['bill_number'])->pluck('id')->first();
@@ -31,10 +35,8 @@ class BillHistories extends Import
return $row;
}
public function rules(): array
public function prepareRules(array $rules): array
{
$rules = (new Request())->rules();
$rules['bill_number'] = 'required|string';
unset($rules['bill_id']);

View File

@@ -11,6 +11,8 @@ use App\Models\Document\DocumentItemTax as Model;
class BillItemTaxes extends Import
{
public $request_class = Request::class;
public function model(array $row)
{
return new Model($row);
@@ -22,6 +24,8 @@ class BillItemTaxes extends Import
return [];
}
$row['bill_number'] = (string) $row['bill_number'];
$row = parent::map($row);
$row['document_id'] = (int) Document::bill()->number($row['bill_number'])->pluck('id')->first();
@@ -44,10 +48,8 @@ class BillItemTaxes extends Import
return $row;
}
public function rules(): array
public function prepareRules(array $rules): array
{
$rules = (new Request())->rules();
$rules['bill_number'] = 'required|string';
unset($rules['bill_id']);

View File

@@ -9,6 +9,8 @@ use App\Models\Document\DocumentItem as Model;
class BillItems extends Import
{
public $request_class = Request::class;
public function model(array $row)
{
return new Model($row);
@@ -20,6 +22,8 @@ class BillItems extends Import
return [];
}
$row['bill_number'] = (string) $row['bill_number'];
$row = parent::map($row);
$row['document_id'] = (int) Document::bill()->number($row['bill_number'])->pluck('id')->first();
@@ -30,6 +34,8 @@ class BillItems extends Import
$row['name'] = $row['item_name'];
}
$row['description'] = !empty($row['item_description']) ? $row['item_description'] : '';
$row['tax'] = (double) $row['tax'];
$row['tax_id'] = 0;
$row['type'] = Document::BILL_TYPE;
@@ -37,10 +43,8 @@ class BillItems extends Import
return $row;
}
public function rules(): array
public function prepareRules(array $rules): array
{
$rules = (new Request())->rules();
$rules['bill_number'] = 'required|string';
unset($rules['bill_id']);

View File

@@ -9,6 +9,8 @@ use App\Models\Document\DocumentTotal as Model;
class BillTotals extends Import
{
public $request_class = Request::class;
public function model(array $row)
{
return new Model($row);
@@ -20,6 +22,8 @@ class BillTotals extends Import
return [];
}
$row['bill_number'] = (string) $row['bill_number'];
$row = parent::map($row);
$row['document_id'] = (int) Document::bill()->number($row['bill_number'])->pluck('id')->first();
@@ -28,10 +32,8 @@ class BillTotals extends Import
return $row;
}
public function rules(): array
public function prepareRules(array $rules): array
{
$rules = (new Request())->rules();
$rules['bill_number'] = 'required|string';
unset($rules['bill_id']);

View File

@@ -8,6 +8,8 @@ use App\Models\Banking\Transaction as Model;
class BillTransactions extends Import
{
public $request_class = Request::class;
public function model(array $row)
{
return new Model($row);
@@ -19,6 +21,8 @@ class BillTransactions extends Import
return [];
}
$row['bill_number'] = (string) $row['bill_number'];
$row = parent::map($row);
$row['type'] = 'expense';
@@ -32,10 +36,8 @@ class BillTransactions extends Import
return $row;
}
public function rules(): array
public function prepareRules(array $rules): array
{
$rules = (new Request())->rules();
$rules['bill_number'] = 'required|string';
return $rules;

View File

@@ -9,6 +9,8 @@ use Illuminate\Support\Str;
class Bills extends Import
{
public $request_class = Request::class;
public function model(array $row)
{
return new Model($row);
@@ -20,6 +22,8 @@ class Bills extends Import
return [];
}
$row['bill_number'] = (string) $row['bill_number'];
$row = parent::map($row);
$country = array_search($row['contact_country'], trans('countries'));
@@ -35,16 +39,14 @@ class Bills extends Import
return $row;
}
public function rules(): array
public function prepareRules(array $rules): array
{
$rules = (new Request())->rules();
$rules['bill_number'] = Str::replaceFirst('unique:documents,NULL', 'unique:documents,document_number', $rules['document_number']);
$rules['billed_at'] = $rules['issued_at'];
$rules['currency_rate'] = 'required';
$rules['currency_rate'] = 'required|gt:0';
unset($rules['document_number'], $rules['issued_at'], $rules['type']);
return $this->replaceForBatchRules($rules);
return $rules;
}
}

View File

@@ -8,6 +8,8 @@ use App\Models\Common\Contact as Model;
class Vendors extends Import
{
public $request_class = Request::class;
public function model(array $row)
{
return new Model($row);
@@ -26,9 +28,4 @@ class Vendors extends Import
return $row;
}
public function rules(): array
{
return (new Request())->rules();
}
}

View File

@@ -3,11 +3,14 @@
namespace App\Imports\Sales;
use App\Abstracts\Import;
use App\Models\Auth\User;
use App\Http\Requests\Common\Contact as Request;
use App\Models\Common\Contact as Model;
class Customers extends Import
{
public $request_class = Request::class;
public function model(array $row)
{
return new Model($row);
@@ -24,11 +27,10 @@ class Customers extends Import
$row['currency_code'] = $this->getCurrencyCode($row);
$row['user_id'] = null;
if (isset($row['can_login']) && isset($row['email'])) {
$row['user_id'] = User::where('email', $row['email'])->first()?->id ?? null;
}
return $row;
}
public function rules(): array
{
return (new Request())->rules();
}
}

View File

@@ -9,6 +9,8 @@ use App\Models\Document\DocumentHistory as Model;
class InvoiceHistories extends Import
{
public $request_class = Request::class;
public function model(array $row)
{
return new Model($row);
@@ -20,6 +22,8 @@ class InvoiceHistories extends Import
return [];
}
$row['invoice_number'] = (string) $row['invoice_number'];
$row = parent::map($row);
$row['document_id'] = (int) Document::invoice()->number($row['invoice_number'])->pluck('id')->first();
@@ -31,10 +35,8 @@ class InvoiceHistories extends Import
return $row;
}
public function rules(): array
public function prepareRules(array $rules): array
{
$rules = (new Request())->rules();
$rules['invoice_number'] = 'required|string';
unset($rules['invoice_id']);

View File

@@ -11,6 +11,8 @@ use App\Models\Document\DocumentItemTax as Model;
class InvoiceItemTaxes extends Import
{
public $request_class = Request::class;
public function model(array $row)
{
return new Model($row);
@@ -22,6 +24,8 @@ class InvoiceItemTaxes extends Import
return [];
}
$row['invoice_number'] = (string) $row['invoice_number'];
$row = parent::map($row);
$row['document_id'] = (int) Document::invoice()->number($row['invoice_number'])->pluck('id')->first();
@@ -44,10 +48,8 @@ class InvoiceItemTaxes extends Import
return $row;
}
public function rules(): array
public function prepareRules(array $rules): array
{
$rules = (new Request())->rules();
$rules['invoice_number'] = 'required|string';
unset($rules['invoice_id']);

View File

@@ -9,6 +9,8 @@ use App\Models\Document\DocumentItem as Model;
class InvoiceItems extends Import
{
public $request_class = Request::class;
public function model(array $row)
{
return new Model($row);
@@ -20,16 +22,20 @@ class InvoiceItems extends Import
return [];
}
$row['invoice_number'] = (string) $row['invoice_number'];
$row = parent::map($row);
$row['document_id'] = (int) Document::invoice()->number($row['invoice_number'])->pluck('id')->first();
if (empty($row['item_id']) && !empty($row['item_name'])) {
if (empty($row['item_id']) && ! empty($row['item_name'])) {
$row['item_id'] = $this->getItemIdFromName($row);
$row['name'] = $row['item_name'];
}
$row['description'] = !empty($row['item_description']) ? $row['item_description'] : '';
$row['tax'] = (double) $row['tax'];
$row['tax_id'] = 0;
$row['type'] = Document::INVOICE_TYPE;
@@ -37,10 +43,8 @@ class InvoiceItems extends Import
return $row;
}
public function rules(): array
public function prepareRules(array $rules): array
{
$rules = (new Request())->rules();
$rules['invoice_number'] = 'required|string';
unset($rules['invoice_id']);

View File

@@ -9,6 +9,8 @@ use App\Models\Document\DocumentTotal as Model;
class InvoiceTotals extends Import
{
public $request_class = Request::class;
public function model(array $row)
{
return new Model($row);
@@ -20,6 +22,8 @@ class InvoiceTotals extends Import
return [];
}
$row['invoice_number'] = (string) $row['invoice_number'];
$row = parent::map($row);
$row['document_id'] = (int) Document::invoice()->number($row['invoice_number'])->pluck('id')->first();
@@ -28,10 +32,8 @@ class InvoiceTotals extends Import
return $row;
}
public function rules(): array
public function prepareRules(array $rules): array
{
$rules = (new Request())->rules();
$rules['invoice_number'] = 'required|string';
unset($rules['invoice_id']);

View File

@@ -8,6 +8,8 @@ use App\Models\Banking\Transaction as Model;
class InvoiceTransactions extends Import
{
public $request_class = Request::class;
public function model(array $row)
{
return new Model($row);
@@ -19,23 +21,23 @@ class InvoiceTransactions extends Import
return [];
}
$row['invoice_number'] = (string) $row['invoice_number'];
$row = parent::map($row);
$row['type'] = 'income';
$row['currency_code'] = $this->getCurrencyCode($row);
$row['account_id'] = $this->getAccountId($row);
$row['category_id'] = $this->getCategoryId($row, 'income');
$row['contact_id'] = $this->getContactId($row, 'customer');
$row['currency_code'] = $this->getCurrencyCode($row);
$row['document_id'] = $this->getDocumentId($row);
$row['number'] = $row['transaction_number'];
return $row;
}
public function rules(): array
public function prepareRules(array $rules): array
{
$rules = (new Request())->rules();
$rules['invoice_number'] = 'required|string';
return $rules;

View File

@@ -9,6 +9,8 @@ use Illuminate\Support\Str;
class Invoices extends Import
{
public $request_class = Request::class;
public function model(array $row)
{
return new Model($row);
@@ -20,6 +22,8 @@ class Invoices extends Import
return [];
}
$row['invoice_number'] = (string) $row['invoice_number'];
$row = parent::map($row);
$country = array_search($row['contact_country'], trans('countries'));
@@ -35,16 +39,14 @@ class Invoices extends Import
return $row;
}
public function rules(): array
public function prepareRules(array $rules): array
{
$rules = (new Request())->rules();
$rules['invoice_number'] = Str::replaceFirst('unique:documents,NULL', 'unique:documents,document_number', $rules['document_number']);
$rules['invoiced_at'] = $rules['issued_at'];
$rules['currency_rate'] = 'required';
$rules['currency_rate'] = 'required|gt:0';
unset($rules['document_number'], $rules['issued_at'], $rules['type']);
return $this->replaceForBatchRules($rules);
return $rules;
}
}

View File

@@ -8,6 +8,8 @@ use App\Models\Setting\Category as Model;
class Categories extends Import
{
public $request_class = Request::class;
public function model(array $row)
{
return new Model($row);
@@ -21,9 +23,4 @@ class Categories extends Import
return $row;
}
public function rules(): array
{
return (new Request())->rules();
}
}

View File

@@ -8,13 +8,10 @@ use App\Models\Setting\Tax as Model;
class Taxes extends Import
{
public $request_class = Request::class;
public function model(array $row)
{
return new Model($row);
}
public function rules(): array
{
return (new Request())->rules();
}
}

View File

@@ -0,0 +1,12 @@
<?php
namespace App\Interfaces\Utility;
use App\Models\Common\Contact;
interface DocumentNumber
{
public function getNextNumber(string $type, ?Contact $contact): string;
public function increaseNextNumber(string $type, ?Contact $contact): void;
}

View File

@@ -0,0 +1,12 @@
<?php
namespace App\Interfaces\Utility;
use App\Models\Common\Contact;
interface TransactionNumber
{
public function getNextNumber(string $type, string $suffix, ?Contact $contact): string;
public function increaseNextNumber(string $type, string $suffix, ?Contact $contact): void;
}

View File

@@ -5,12 +5,15 @@ namespace App\Jobs\Auth;
use App\Abstracts\Job;
use App\Models\Auth\UserInvitation;
use App\Notifications\Auth\Invitation as Notification;
use App\Traits\Sources;
use Exception;
use Illuminate\Support\Str;
use Symfony\Component\Mailer\Exception\TransportException;
class CreateInvitation extends Job
{
use Sources;
protected $invitation;
protected $user;
@@ -23,9 +26,17 @@ class CreateInvitation extends Job
public function handle(): UserInvitation
{
\DB::transaction(function () {
$invitations = UserInvitation::where('user_id', $this->user->id)->get();
foreach ($invitations as $invitation) {
$invitation->delete();
}
$this->invitation = UserInvitation::create([
'user_id' => $this->user->id,
'token' => (string) Str::uuid(),
'created_by' => user_id(),
'created_from' => $this->getSourceName(request()),
]);
$notification = new Notification($this->invitation);

View File

@@ -12,6 +12,10 @@ class UpdateRole extends Job implements ShouldUpdate
{
public function handle(): Role
{
if (in_array($this->model->name, config('roles.defaults', ['admin', 'manager', 'accountant', 'employee']))) {
$this->request->name = $this->model->name;
}
event(new RoleUpdating($this->model, $this->request));
\DB::transaction(function () {

Some files were not shown because too many files have changed in this diff Show More