Untitled

mail@pastecode.io avatarunknown
php
a month ago
34 kB
2
Indexable
Never
<?php

namespace App\Http\Controllers\v2;

use App\Http\Controllers\ApiController;
use App\Http\Requests\AdtafRequest;
use App\Http\Requests\CerfRequest;
use App\Http\Requests\RequisitionItem\PrItemSetOtherChargesGroupRequest;
use App\Http\Requests\RequisitionItem\PrItemUpdateAccountAssignmentRequest;
use App\Http\Requests\RequisitionItem\PrItemSetCostCenterRequest;
use App\Http\Requests\RequisitionItem\PrItemSetDiscountRequest;
use App\Http\Requests\RequisitionItem\PrItemSetOtherChargesRequest;
use App\Http\Requests\RequisitionItem\PrItemSetPriceRequest;
use App\Http\Requests\RequisitionItem\PrItemSetQuantityRequest;
use App\Http\Requests\RequisitionItem\PrItemSetTaxRequest;
use App\Metabuyer\CompanyItem\Services\CompanyItemService;
use App\Metabuyer\Enumerations\RoleName;
use App\Metabuyer\PR\Services\PrDocumentRuleCreationService;
use App\Metabuyer\Requisition\Enums\ItemAction;
use App\Metabuyer\Requisition\Models\NonCatalogItem;
use App\Metabuyer\Requisition\Models\RequisitionItemV2;
use App\Metabuyer\Requisition\Requests\PrItemUpdateNonCatalogRequest;
use App\Metabuyer\Requisition\Requests\PrItemSetFreeOfChargeRequest;
use App\Metabuyer\Requisition\Requests\PrItemSetNoOtherQuotationAvailableRequest;
use App\Metabuyer\Requisition\Requests\PrItemSetProjectCodeRequest;
use App\Metabuyer\Requisition\Services\RequisitionItemAccountAssignmentService;
use App\Metabuyer\Requisition\Services\RequisitionItemPostProcessorService;
use App\Metabuyer\Requisition\Transformers\RequisitionTransformer;
use App\Metabuyer\AssetCategory\Services\AssetCategoryService;
use App\Metabuyer\Requisition\Services\RequisitionItemService;
use Carbon\Carbon;
use Illuminate\Support\Facades\Input;
use Metabuyer\Enumerations\ExpenseTypeCategory;
use Metabuyer\Enumerations\MasterDataActivity;
use Metabuyer\Helpers\FileUtils;
use Metabuyer\Models\AccountCode;
use Metabuyer\Models\Company;
use Metabuyer\Models\CostCenter;
use Metabuyer\Models\Tax;
use Metabuyer\Models\Currencies;
use Metabuyer\PR\Enum\PrDocumentRuleEnum;
use Metabuyer\PR\Enum\OtherCharges;
use Metabuyer\PR\PRException;
use Metabuyer\PR\Enum\PRStatus;
use Metabuyer\Security\SecurityUtils;
use Metabuyer\AbstractModel\MetadataHelper;
use App\Metabuyer\Models\Quotation;
use MongoDB\BSON\ObjectId;
use App\Metabuyer\Tender\Services\TenderPrIntegrationService;
use Metabuyer\Services\StorageService;
use App\Metabuyer\CatalogV2\Enums\CatalogUploadPath;
use Illuminate\Http\Request;

/**
 * Class RequisitionItemController
 *
 * This is the new controller for all requisition item v2
 *
 * @package App\Http\Controllers
 * @author Ling Nai Shin <ling_naishin@hotmail.com>
 * @copyright 2020 Metacloud Sdn. Bhd.
 */
class RequisitionItemController extends ApiController
{
    /**
     * To set pr item quantity
     *
     * @param PrItemSetQuantityRequest $request           request
     * @param string                   $requisitionItemId pr item id
     * @return \Illuminate\Http\Response
     * @throws PRException
     */
    public function setQuantity(PrItemSetQuantityRequest $request, $requisitionItemId)
    {
        $prItem = RequisitionItemV2::findOne($requisitionItemId);
        if (empty($prItem)) {
            throw new PRException('PR Item line could not be retrieved.', -1);
        }

        $prItem->getPr()->beforeUpdate();

        $quantity = $request->get('qty');
        if (!empty($prItem->uom) && $prItem->uom['is_fraction'] == 0 && floor($quantity) != $quantity) {
            throw new PRException('Quantity of the item\'s UOM cannot be a fraction.', -1);
        }
        if ($prItem->discount > $quantity * $prItem->unit_price) {
            throw new PRException('Item discount cannot be greater than item amount.', -1);
        }
        $prItem->setQuantity($quantity);
        $prItem->saveToPr(true);

        return $this->respondWithItem($prItem->getPr(), new RequisitionTransformer());
    }

    /**
     * Set price
     *
     * @param PrItemSetPriceRequest $request           request
     * @param string                $requisitionItemId id
     * @return \Illuminate\Http\Response
     */
    public function setPrice(PrItemSetPriceRequest $request, $requisitionItemId)
    {
        $prItem = RequisitionItemV2::findOne($requisitionItemId);
        if (empty($prItem)) {
            throw new PRException('PR Item line could not be retrieved.', -1);
        }

        $prItem->getPr()->beforeUpdate();

        $price = $request->get('price');

        $allowDecimal = $prItem->currency["allow_decimal"];
        if (isset($allowDecimal) && !$allowDecimal) {
            if (strpos(strval($price), '.')) {
                throw new PRException('Unit price cannot be a decimal value', -1);
            }
        }

        if ($prItem->is_catalog_item == true && $prItem->is_partial != 1) {
            throw new PRException('Cannot edit the price for a catalog non-partial item.', -1);
        }
        if (!preg_match('/^\d{0,17}(\.\d{0,4})?$/', $price)) {
            throw new PRException('Price can only be maximum 4 decimal places.', -1);
        }

        if ($prItem->discount > $prItem->qty * $price) {
            throw new PRException('Item discount cannot be greater than item amount.', -1);
        }
        $prItem->setPrice($price, $requisitionItemId);
        $prItem->saveToPr(true, true);

        return $this->respondWithItem($prItem->getPr(), new RequisitionTransformer());
    }


    /**
     * Set discount
     *
     * @param PrItemSetDiscountRequest $request           request
     * @param string                   $requisitionItemId id
     *
     * @return \Illuminate\Http\Response
     * @throws PRException
     * @throws \Exception
     */
    public function setDiscount(PrItemSetDiscountRequest $request, $requisitionItemId)
    {
        $prItem = RequisitionItemV2::findOne($requisitionItemId);
        if (empty($prItem)) {
            throw new PRException('PR Item line could not be retrieved.', -1);
        }

        $prItem->getPr()->beforeUpdate();

        $discount = $request->get('discount', 0);
        $isPercentage = $request->get('is_percentage', false);

        $allowDecimal = $prItem->currency['allow_decimal'];
        if ((isset($allowDecimal) && !$allowDecimal) && !$isPercentage) {
            if (strpos(strval($discount), '.')) {
                throw new PRException('Line discount cannot be a decimal value', -1);
            }
        }

        // this will allow removal of discount if added by user previously
        $documentRuleService = new PrDocumentRuleCreationService();
        $lineDiscountNotAllowed =
            $documentRuleService->getDocumentRule($prItem->getPr(), PrDocumentRuleEnum::ALLOW_LINE_DISCOUNT) === false;
        if ($lineDiscountNotAllowed && !empty($discount)) {
            throw new PRException('Line discount is not allowed.', -1);
        }

        if ($isPercentage) {
            if ($discount > 100) {
                throw new PRException('Item discount percentage cannot be greater than 100%', -1);
            }
        } else {
            if ($discount > $prItem->qty * $prItem->unit_price) {
                throw new PRException('Item discount cannot be greater than item amount.', -1);
            }
        }

        $prItem->setDiscount($discount, $isPercentage);
        $prItem->saveToPr(true, true);

        return $this->respondWithItem($prItem->getPr(), new RequisitionTransformer());
    }

    /**
     * Set tax
     *
     * @param PrItemSetTaxRequest $request           request
     * @param string              $requisitionItemId id
     * @return \Illuminate\Http\Response
     * @throws PRException
     */
    public function setTax(PrItemSetTaxRequest $request, $requisitionItemId)
    {
        $prItem = RequisitionItemV2::findOne($requisitionItemId);
        if (empty($prItem)) {
            return $this->respondError('PR Item line could not be retrieved.');
        }
        $taxId = $request->get('tax_id', null);
        $tax = Tax::findById($taxId);

        $prItem->setTax($tax);
        $prItem->saveToPr(true);

        return $this->respondWithItem($prItem->getPr(), new RequisitionTransformer());
    }

    /**
     * Set other charges in PR Item
     * Only able to set quantity, unit price, discount, and tax code
     *
     * @param PrItemSetOtherChargesRequest $request           Request
     * @param string                       $requisitionItemId PR item ID
     * @return \Illuminate\Http\Response
     * @throws \Exception
     */
    public function setOtherCharges(PrItemSetOtherChargesRequest $request, string $requisitionItemId)
    {
        $prItem = RequisitionItemV2::findOne($requisitionItemId);
        if (empty($prItem)) {
            throw new PRException('PR item not found.', -1);
        }

        $allowDecimal = Currencies::getCurrencyByCode($prItem['currency']['code'])->allow_decimal;
        if (isset($allowDecimal) && !$allowDecimal) {
            if (strpos(strval($request['amount']), '.')) {
                throw new PRException('Other charge unit price cannot be a decimal value', -1);
            }
            if (strpos(strval($request['discount_amount']), '.')) {
                throw new PRException('Other charge discount cannot be a decimal value', -1);
            }
        }

        if (!empty($request['discount_percentage']) && $request['discount_percentage'] > 100) {
            throw new PRException('Other charge discount percentage cannot be greater than 100%', -1);
        }

        if ($request['other_charges'] === 'miscellaneous' && strlen($request['remark']) > 1000) {
            throw new PRException('Miscellaneous description may not be greater than 1000 characters', -1);
        }

        $prItem->getPr()->beforeUpdate();

        $prItem->setOtherCharges($request->get('other_charges'), $request->all());
        $prItem->saveToPr(true, true);

        return $this->respondWithItem($prItem->getPr(), new RequisitionTransformer());
    }

    /**
     * remove other charges
     *
     * @param string $requisitionItemId requisition item id
     * @param string $field             other charges field
     * @return \Illuminate\Http\Response
     * @throws PRException
     * @throws \Exception
     */
    public function removeOtherCharges(string $requisitionItemId, string $field)
    {
        $prItem = RequisitionItemV2::findOne($requisitionItemId);
        if (empty($prItem)) {
            throw new PRException('PR item not found.', -1);
        }

        if (!in_array($field, OtherCharges::VALID_VALUES)) {
            throw new PRException('Other Charges field not found.', -1);
        }

        if (!empty($prItem->other_charges_group)) {
            return $this->respondError('Cannot remove or edit other charges when other charges group is selected.');
        }

        $prItem->removeOtherCharges($field);
        $prItem->saveToPr(true, true);

        return $this->respondWithItem($prItem->getPr(), new RequisitionTransformer());
    }

    /**
     * Add account code to pr item
     *
     * @param PrItemUpdateAccountAssignmentRequest $request           Request
     * @param string                               $requisitionItemId pr item id
     * @return \Illuminate\Http\Response
     * @throws \Metabuyer\PR\PRException
     * @throws \Exception
     */
    public function updateAccountAssignment(PrItemUpdateAccountAssignmentRequest $request, $requisitionItemId)
    {
        $prItem = RequisitionItemV2::findOne($requisitionItemId);
        if (empty($prItem)) {
            throw new PRException('PR item not found.', -1);
        }
        $pr = $prItem->getPr();
        $pr->beforeUpdate();

        $data = $request->get('account_assignments');

        $service = app(RequisitionItemAccountAssignmentService::class);
        $data = $service->prepareAccountAssignmentData($data, $pr, true);
        $prItem = $service->updateAccountAssignment($prItem, $data);
        $prItem->saveToPr(true);

        return $this->respondWithItem($pr, new RequisitionTransformer());
    }

    /**
     * Set cost center
     *
     * @param PrItemSetCostCenterRequest $request           request
     * @param string                     $requisitionItemId item id
     * @return \Illuminate\Http\Response
     * @throws \Exception
     */
    public function setCostCenter(PrItemSetCostCenterRequest $request, $requisitionItemId)
    {
        $prItem = RequisitionItemV2::findOne($requisitionItemId);
        if (empty($prItem)) {
            throw new PRException('PR Item line could not be retrieved.', -1);
        }
        $pr = $prItem->getPr();
        $costCenterId = $request->get('cost_center')['_id'] ?? [];

        if (!empty($pr->header_info->cost_center) && !empty($pr->header_info->cost_center['_id'])) {
            if ($pr->header_info->cost_center['_id'] !== $costCenterId) {
                throw new PRException('PR line item cost center must be the same in header.', -1);
            }
        }

        if (!empty($costCenterId)) {
            $cc = CostCenter::find($costCenterId);
            $prItem->setCostCenter($cc);
            $prItem->saveToPr(false);
        } else {
            $prItem->setCostCenter();
            $prItem->saveToPr(false);
        }

        return $this->respondWithItem($prItem->getPr(), new RequisitionTransformer());
    }

    /**
     * Update non catalog item
     *
     * @param PrItemUpdateNonCatalogRequest $request           Request object
     * @param string                        $requisitionItemId item id
     * @return \Illuminate\Http\Response
     * @throws PRException
     * @throws \Exception
     */
    public function updateNonCatalogItem(PrItemUpdateNonCatalogRequest $request, string $requisitionItemId)
    {
        $prItem = RequisitionItemV2::findOne($requisitionItemId);
        if (!$prItem instanceof RequisitionItemV2) {
            throw new PRException('PR Item line could not be retrieved.', -1);
        }
        $pr = $prItem->getPr();

        if (isSystemEnableV2()) {
            $tenderPrIntegrationService = app(TenderPrIntegrationService::class);
            $tenderPrIntegrationService->checkNonCatalogAction($pr, $request->supplier_code);
        }
        
        $companyItemService = app(CompanyItemService::class);
        // validates item assigned to company
        $itemMasterCode = $request->get('item_code');
        $companyCode = $pr->header_info->company['code'];
        $companyItem = $companyItemService->getSingleByItemMasterCodeAndCompanyCode(
            $itemMasterCode,
            $companyCode,
            []
        );
        if (empty($companyItem)) {
            throw new PRException('Item not assigned to company', -1);
        }

        // validates company item expense type category matches pr's
        if ($companyItem->expense_type_category !== $pr->getExpenseTypeField('category')) {
            throw new PRException('Company item expense type does not match PR expense type', -1);
        }

        $fields = [
            'item_code', // Checks company_item, Takes name, descr, item_category and is_stock
            'category_code',
            'descr',
            'cost_center_code',
            'supplier_code',
            'currency_code',
            'uom_code',
            'qty',
            'unit_price',
            'discount',
            'discount_percentage',
            'payment_term_code',
            'tax_code',
            'budget_id',
            'product_availability',
            'supplier_part_no',
            'lead_time',
            'supplier_reference_no',
            'quotation_validity_from',
            'quotation_validity_to',
            'justification',
            'note_to_supplier',
            'packing_info',
            'no_pricing_available',
            'foc_item',
            'img_url',
            'other_charges',
            'cerf',
            'adtaf',
            'account_assignments',
            'project_code',
            'no_other_quotation_available',
            'other_charges_group_uuid',
            'other_charges_group',
            'freight',
            'insurance',
            'transportation',
            'bahan_bakar_tax',
            'withholding_tax',
            'miscellaneous',
            'needed_by_date',
            'delivery_address'
        ];

        $data = $request->only($fields);

        $data['qty'] = (float)$data['qty'];
        $data['quotation_validity_from'] = Carbon::createFromTimestampMs($data['quotation_validity_from']);
        $data['quotation_validity_to'] = Carbon::createFromTimestampMs($data['quotation_validity_to']);

        // other charges flattening for RequisitionOtherCharges::generate
        if (!empty($data['other_charges'])) {
            foreach ($data['other_charges'] as $name => $otherCharge) {
                if (!empty($otherCharge['discount_amount'] && str_contains($otherCharge['discount_amount'], '%'))) {
                    $otherCharge['discount_percentage'] = (float)str_replace('%', '', $otherCharge['discount_amount']);
                }
                // Exchange rate section
                $exchangeRate = $pr->getItemExchangeRate($otherCharge);
                $otherCharge['exchange_rate'] = $exchangeRate;

                // Tax section
                if (isset($otherCharge['tax_id'])) { // means setting the tax now
                    $tax = Tax::find($otherCharge['tax_id']);
                    $otherCharge['tax'] = MetadataHelper::getAttributesToArchive($tax);
                } elseif (isset($otherChargesData['tax'])) {
                    // means the tax already set and need only to refresh the calculation
                    // $outputArray['tax'] = $otherChargesData['tax'];
                    if (isset($otherChargesData['tax']['_id']) && ! empty($otherChargesData['tax'])) {
                        $tax = Tax::find($otherChargesData['tax']['_id']);
                        $otherCharge['tax'] = MetadataHelper::getAttributesToArchive($tax);
                    } else {
                        $otherCharge['tax'] = [];
                    }
                }

                $data[$name] = $otherCharge;
            }
            unset($data['other_charges']);
        }

        // if currency is not set, currency should be default to header's
        if (empty($data['currency_code'])) {
            $data['currency_code'] = $pr->header_info->currency['code'];
        }

        // TODO: A few fields need to have their types converted and / or defaults given:
        // - the "number" fields like unit_price or lead_time
        // - boolean fields (no_pricing_available, foc_item)
        //   - probably should make the payload and validation 1 or 0, then cast it to boolean

        if (empty($data['project_code'])) {
            $companySetting = Company::getCompanyByCode($pr->header_info->company['code']);
            $prExpenseType = $pr->getExpenseTypeField('category');
            $isSettingEnabled = $companySetting->requisition_settings['project_code_capex_mandatory']['is_enabled'] ??
                false;

            if ($isSettingEnabled === true && $prExpenseType === ExpenseTypeCategory::CAPEX) {
                throw new PRException('Project Code is mandatory for this company', -1);
            }
        }

        $nonCatalogItem = new NonCatalogItem($data);
        $newPrItem = (new RequisitionItemV2($pr))->initialize($nonCatalogItem);
        $newPrItem->_id = $prItem->_id;
        $newPrItem->uuid = $prItem->uuid;
        #images have different API
        $newPrItem->img_url = $prItem->img_url;
        $newPrItem->string_id = $prItem->string_id;

        if (!empty($data['other_charges_group'])) {
            $newPrItem->other_charges_group = $data['other_charges_group'];
        }
        if (!empty($data['freight'])) {
            $newPrItem->freight = $data['freight'];
        }
        if (!empty($data['insurance'])) {
            $newPrItem->insurance = $data['insurance'];
        }
        if (!empty($data['transportation'])) {
            $newPrItem->transportation = $data['transportation'];
        }
        if (!empty($data['bahan_bakar_tax'])) {
            $newPrItem->bahan_bakar_tax = $data['bahan_bakar_tax'];
        }
        if (!empty($data['withholding_tax'])) {
            $newPrItem->withholding_tax = $data['withholding_tax'];
        }
        if (!empty($data['miscellaneous'])) {
            $newPrItem->miscellaneous = $data['miscellaneous'];
        }

        if (isset($prItem->cerf)) {
            $newPrItem->cerf = $prItem->cerf;
        }
        if (isset($prItem->adtaf)) {
            $newPrItem->adtaf = $prItem->adtaf;
        }
        if (isset($data['foc_item'])) {
            $newPrItem->setFreeOfCharge($data['foc_item']);
        }
        if (isset($prItem->account_assignments)) {
            $newPrItem->account_assignments = $prItem->account_assignments;
        }
        if (isset($newPrItem->budget)) {
            $RequisitionitemService = app(RequisitionItemService::class);
            $RequisitionitemService->validateAndSetBudget($newPrItem, $newPrItem->budget);
        }
        if (isset($prItem->no_other_quotation_available)) {
            $newPrItem->no_other_quotation_available = $prItem->no_other_quotation_available;
        }
        if (isset($prItem->cost_center)) {
            $newPrItem->cost_center = $prItem->cost_center;
        }
        if (isset($newPrItem->project_code)) {
            $newPrItem->setProjectCode($newPrItem->project_code);
        }

        $newPrItem->saveToPr(true, true);

        return $this->respondWithItem($newPrItem->getPr(), new RequisitionTransformer());
    }

    /**
     * Set note to supplier
     *
     * @param string $requisitionItemId pr item id
     * @return \Illuminate\Http\Response
     */
    public function setNoteToSupplier($requisitionItemId)
    {
        $prItem = RequisitionItemV2::findOne($requisitionItemId);
        $note = Input::get('note_to_supplier');
        if (isset($note) && !is_string($note)) {
            throw new PRException('Invalid note.', -1);
        }
        if (strlen($note) > 850) {
            throw new PRException('Note to supplier may not be greater than 850 characters.', -1);
        }
        $prItem->setNoteToSupplier($note);
        $prItem->saveToPr(false);

        return $this->respondWithItem($prItem->getPr(), new RequisitionTransformer());
    }

    /**
     * Update the Image in a nonCatalog item
     *
     * @param string $requisitionItemId id
     * @return \Illuminate\Http\Response
     */
    public function updateNonCatalogItemImage($requisitionItemId, Request $request)
    {
        $filesystemDisk = config('filesystems.default');
        $prItem = RequisitionItemV2::findOne($requisitionItemId);
        if (empty($prItem)) {
            throw new PRException('PR Item line could not be retrieved.', -1);
        }
        $pr = $prItem->getPr();

        $imageFile = Input::file('itemImage');
        if (!empty($imageFile)) {

            $fileContent = file_get_contents(Input::file('itemImage'));
            $file = $request->file('itemImage');
            $fileExt = $file->getClientOriginalExtension();
            $fileName = $requisitionItemId . $pr->string_id;
            $fileName = str_replace(' ', '-', $fileName);
            $fileNameExt = $fileName.'.'.$fileExt;

            if ($filesystemDisk === 'local') {
                $imageUrl = FileUtils::createItemImage($imageFile, $fileNameExt);
            } else {
                StorageService::upload(CatalogUploadPath::IMAGES, $fileNameExt, $fileContent);
                $imageUrl = $fileNameExt;
            }
            
            if ($imageUrl == false) {
                return $this->errorWrongArgs('Invalid Image File');
            }
            $prItem->addImageUrl($imageUrl);
            $prItem->savetoPr(false);

            return $this->respondWithItem($pr, new RequisitionTransformer());
        }
    }

    /**
     * Set free of charge
     *
     * @param PrItemSetFreeOfChargeRequest $request           request
     * @param string                       $requisitionItemId pr item id
     * @return \Illuminate\Http\Response
     * @throws \Exception
     */
    public function setFreeOfCharge(PrItemSetFreeOfChargeRequest $request, $requisitionItemId)
    {
        $prItem = RequisitionItemV2::findOne($requisitionItemId);
        if (empty($prItem)) {
            throw new PRException('PR Item line could not be retrieved.', -1);
        }

        $prItem->getPr()->beforeUpdate();

        $prItem->setFreeOfCharge($request->get('foc'));
        $prItem->savetoPr(true);

        return $this->respondWithItem($prItem->getPr(), new RequisitionTransformer());
    }

    /**
     * Set no other quotation available
     *
     * @param PrItemSetNoOtherQuotationAvailableRequest $request           request
     * @param string                                    $requisitionItemId pr item id
     * @return \Illuminate\Http\Response
     * @throws \Exception
     */
    public function setNoOtherQuotationAvailable(PrItemSetNoOtherQuotationAvailableRequest $request, $requisitionItemId)
    {
        $prItem = RequisitionItemV2::findOne($requisitionItemId);
        if (empty($prItem)) {
            throw new PRException('PR Item line could not be retrieved.', -1);
        }

        $data = [
            'status' => $request->get('status'),
            'reason' => $request->get('reason') ?? '',
        ];

        $prItem->getPr()->beforeUpdate();

        $prItem->setNoOtherQuotationAvailable($data);
        $prItem->savetoPr(true);

        return $this->respondWithItem($prItem->getPr(), new RequisitionTransformer());
    }

    /**
     * Set project code
     *
     * @param PrItemSetProjectCodeRequest $request           request
     * @param string                      $requisitionItemId pr item id
     * @return \Illuminate\Http\Response
     * @throws PRException
     * @throws \Exception
     */
    public function setProjectCode(PrItemSetProjectCodeRequest $request, $requisitionItemId)
    {
        $prItem = RequisitionItemV2::findOne($requisitionItemId);
        if (empty($prItem)) {
            throw new PRException('PR Item line could not be retrieved.', -1);
        }

        $prItem->getPr()->beforeUpdate();

        $prItem->setProjectCode($request->get('project_code'));
        $prItem->savetoPr(true);

        return $this->respondWithItem($prItem->getPr(), new RequisitionTransformer());
    }

    /**
     * * create/update cerf
     *
     * @param CerfRequest $request           request
     * @param string      $requisitionItemId pr item id
     * @return \Illuminate\Http\JsonResponse
     * @throws \Exception
     */
    public function manageCerf(CerfRequest $request, string $requisitionItemId)
    {
        $user = SecurityUtils::getCurrentUser();

        $parameterFields = [
            'asset_category',
            'purpose_of_acquisition_1',
            'purpose_of_acquisition_2',
            'type_of_acquisition',
            'capex_evaluation_1',
            'capex_evaluation_2',
            'useful_life',
            'asset_to_be_located',
            'asset_description',
            'remarks',
            'replacement_asset_main_number',
            'replacement_asset_sub_number',
            'asset_main_number',
            'asset_sub_number',
            'company_code'
        ];

        $input = $request->intersect($parameterFields);

        $prItem = RequisitionItemV2::findOne($requisitionItemId);
        if (empty($prItem)) {
            throw new PRException('PR Item line could not be retrieved', -1);
        }

        $company = Company::find($prItem->getPr()->header_info['company']['_id']);
        $isFinanceVerifierForCompany = $user->hasAnyRole([RoleName::FINANCE_VERIFIER], $company);

        if (!$isFinanceVerifierForCompany) {
            $prItem->getPr()->beforeUpdate();
        }

        if ($prItem->getPr()->getExpenseTypeField('category') !== ExpenseTypeCategory::CAPEX) {
            throw new PRException('Only CAPEX item can add CERF', -1);
        }

        if (!empty($input['asset_category']['code'])) {
            $assetCategoryCode = $input['asset_category']['code'];
            $assetCategory = app(AssetCategoryService::class)->getSingleByCode(
                $assetCategoryCode,
                $input['company_code'],
                ['code', 'name', 'account_code', 'asset_useful_life']
            );

            if (empty($assetCategory)) {
                throw new PRException('Fail to retrieve Asset Category Data', -1);
            }

            // check account code status.
            $accountCode = AccountCode::getAccountCodeByCode($input['company_code'], $assetCategory['account_code']);
            if ($accountCode['is_active'] === MasterDataActivity::DEACTIVATED) {
                throw new PRException('Account Code for ' . $assetCategory['code'] . ' is inactive.', -1);
            } else {
                $input['assetCategory']['code'] = $assetCategory['code'];
                $input['assetCategory']['name'] = $assetCategory['name'];
                $input['assetCategory']['account_code'] = $assetCategory['account_code'];
                $input['assetCategory']['asset_useful_life'] = $input['asset_category']['asset_useful_life'];
            }
        }

        $postProcessorService = (new RequisitionItemPostProcessorService($prItem));

        if (empty($prItem->cerf)) {
            $prItem->manageCerf($input, true);
            $prItem = $postProcessorService->run(ItemAction::CREATE_CERF);
        } else {
            //validation for Finance Verfier when changing Asset Category
            $accountCodeCategoryTypeCode = !empty($accountCode['category_type']) ?
                $accountCode['category_type']['code'] : '';
            $currentCategoryTypeCode = !empty($prItem->account_assignments[0]['account_code']['category_type']) ?
                $prItem->account_assignments[0]['account_code']['category_type']['code'] : '';
            if (($accountCodeCategoryTypeCode !== $currentCategoryTypeCode) &&
                $prItem->getPr()->header_info['status'] !== PRStatus::DRAFT) {
                throw new PRException('Unable to change Asset Category due to data conflict', -1);
            }

            $prItem->manageCerf($input, false);
            $prItem = $postProcessorService->run(ItemAction::UPDATE_CERF);
        }

        $prItem->saveToPr(false);

        return $this->respondWithItem($prItem->getPr(), new RequisitionTransformer());
    }

    /**
     * * create/update ADTAF
     *
     * @param AdtafRequest $request           request
     * @param string       $requisitionItemId pr item id
     * @return \Illuminate\Http\JsonResponse
     * @throws \Exception
     */
    public function manageAdtaf(AdtafRequest $request, string $requisitionItemId)
    {
        $user = SecurityUtils::getCurrentUser();

        $parameterFields = [
            'cost_of_purchase',
            'mode_of_disposal',
            'estimate_sales',
            'transferee_cost_centre',
            'reason',
            'transfer_cost',
            'transferee_company_code',
            'transfer_quantity',
            'remarks',
            'reason_explanation'
        ];

        $input = $request->intersect($parameterFields);

        $prItem = RequisitionItemV2::findOne($requisitionItemId);
        if (empty($prItem)) {
            throw new PRException('PR Item line could not be retrieved', -1);
        }

        $company = Company::find($prItem->getPr()->header_info['company']['_id']);
        $isFinanceVerifierForCompany = $user->hasAnyRole([RoleName::FINANCE_VERIFIER], $company);

        if (!$isFinanceVerifierForCompany) {
            $prItem->getPr()->beforeUpdate();
        }

        if ($prItem->getPr()->getExpenseTypeField('category') !== ExpenseTypeCategory::CAPEX) {
            throw new PRException('Only CAPEX item can add ADTAF', -1);
        }

        if (empty($prItem->cerf)) {
            throw new PRException('Please add CERF item before adding ADTAF', -1);
        }

        // TODO: use enum for type of acquiaition (Shin, 23/6/2020)
        if ($prItem->cerf['type_of_acquisition'] !== 'Replacement') {
            throw new PRException('Only CERF with type of acquisition "Replacement" can add ADTAF', -1);
        }

        if (empty($prItem->adtaf)) {
            $prItem->manageAdtaf($input, true);
        } else {
            $prItem->manageAdtaf($input, false);
        }

        $prItem->saveToPr(false);

        return $this->respondWithItem($prItem->getPr(), new RequisitionTransformer());
    }

    /**
     * Set other charges group
     *
     * @param PrItemSetOtherChargesGroupRequest $request           request
     * @param string                            $requisitionItemId item id
     * @return \Illuminate\Http\Response
     * @throws \Exception
     */
    public function setOtherChargesGroup(PrItemSetOtherChargesGroupRequest $request, string $requisitionItemId)
    {
        $prItem = RequisitionItemV2::findOne($requisitionItemId);
        if (empty($prItem)) {
            throw new PRException('PR Item line could not be retrieved', -1);
        }

        $prItem->getPr()->beforeUpdate();

        $data = $request->all();
        if (empty($data['other_charges_group'])) {
            $prItem->removeOtherChargesGroup();
        } else {
            $prItem->setOtherChargesGroup($data, $prItem);
        }
        return $this->respondWithItem($prItem->getPr(), new RequisitionTransformer());
    }

    /**
     * set justification
     *
     * @param string $requisitionItemId requisition item id
     * @return \Illuminate\Http\Response
     * @throws PRException
     * @throws \Exception
     */
    public function setJustification(string $requisitionItemId)
    {
        $prItem = RequisitionItemV2::findOne($requisitionItemId);
        if (empty($prItem)) {
            throw new PRException('PR Item line could not be retrieved', -1);
        }

        $justification = Input::get('justification');

        if (isset($justification) && !is_string($justification)) {
            throw new PRException('Invalid $justification.', -1);
        }

        $prItem->setJustification($justification);
        $prItem->saveToPr(false);

        return $this->respondWithItem($prItem->getPr(), new RequisitionTransformer());
    }
}