File: /home/assibfaf/public_html/prox-classes/PriceCalculator.php
<?php
/**
* The base configuration for Proxim
* @package Proxim
* @author Davison Pro <davisonpro.coder@gmail.com | https://davisonpro.dev>
* @copyright 2019 Proxim
* @version 1.0.0
* @since File available since Release 1.0.0
*/
namespace Proxim;
use Exception;
use Proxim\Util\ArrayUtils;
/**
* @property {{
* type_id: number|string,
* value: number|null,
* quantity: number,
* service_type_id: number,
* writer_category_id: number|null,
* }} WinbackCoupon
*
* @property {{
* pages: number,
* slides: number,
* discount: object,
* charts: number,
* winbackCoupons: Array<WinbackCoupon>,
* writerCategoryId: number|null,
* tariffPricePerPage: number,
* tariffHrs: number,
* spacing: string|null,
* getSamplesOn: boolean,
* getProgressiveDeliveryOn: boolean,
* getUsedSourcesOn: boolean,
* writerPercent: number,
* complexAssignmentDiscipline: boolean,
* expertProofreading: boolean,
* }} OrderInfo
*
* @property {{
* pages: number,
* slides: number,
* charts: number,
* progressiveDelivery: boolean,
* copyOfSources: boolean,
* writerSamples: boolean,
* categoriesOfWriter: Array<number>,
* }} FreeThings
*
* @property {{
* servicesById: Object.<string, Service>,
* totalCost: number,
* }} CostInfo
*
* @property {{
* quantity: number,
* type_id: number|string,
* title: string,
* cost: number,
* winbackQuantity: number,
* winbackReduction: number,
* priority: number,
* price: number,
* itemPrice: number,
* pricePercent: number,
* forcedReason: string|null,
* disabledReason: string|null,
* active: boolean,
* operations: Array,
* }} Service
*/
class PriceCalculator {
const ukForm = false;
const PROVIDE_SAMPLES_PRICE = 5;
const PROGRESSIVE_DELIVERY_PERCENT = 10;
const USED_SOURCES_MIN_PRICE = 14.95;
const USED_SOURCES_PERCENT = 10;
const COUPONS_TYPES_IDS = [
'PERCENT' => 1,
'AMOUNT' => 3,
'FREE_UNIT' => 5,
'WRITER_CATEGORY' => 7,
'HALF_CUT_DEADLINE' => 8,
'PRICE_PER_PAGE' => 9
];
const COMPLEX_ASSIGNMENT_PERCENT = 20;
const EXPERT_PROOFREADING_PERCENT = 25;
const DRAFT_OUTLINE_PERCENT = 25;
const PLAGIARISM_REPORT_PRICE = 9.99;
const VIP_SUPPORT_PRICE = 9.99;
const SERVICES_IDS = [
'WRITING_PAGES' => 1,
'WRITING_SLIDES' => 2,
'WRITING_CHARTS' => 31,
'CHOOSE_WRITER' => 3,
'PROVIDE_ME_SAMPLES' => 4,
'PROGRESSIVE_DELIVERY' => 5,
'DISCOUNT' => 6,
'USED_SOURCES' => 21,
'COMPLEX_ASSIGNMENT' => 37,
'REVISION_MINOR_PAGES' => 26,
'REVISION_MINOR_SLIDES' => 27,
'REVISION_MINOR_CHARTS' => 33,
'REVISION_MAJOR_PAGES' => 28,
'REVISION_MAJOR_SLIDES' => 29,
'REVISION_MAJOR_CHARTS' => 35,
'EXPERT_PROOFREADING' => 41,
'ADD_BOOST_CATEGORY' => 42,
'ADD_SHORTEN_DEADLINE' => 43,
'VIP_SUPPORT' => 44,
'PLAGIARISM_REPORT' => 45,
'DRAFT_OUTLINE' => 46
];
const ADD_SERVICES_IDS = [
'ADD_WRITING_PAGES' => 'page',
'ADD_WRITING_SLIDES' => 'slide',
'ADD_WRITING_CHARTS' => 'chart',
'ADD_PROVIDE_ME_SAMPLES' => 'samples',
'ADD_PROGRESSIVE_DELIVERY' => 'progressive',
'ADD_USED_SOURCES' => 'sources',
'ADD_BOOST_CATEGORY' => 'boost',
'ADD_SHORTEN_DEADLINE' => 'shorten',
'ADD_EXPERT_PROOFREADING' => 'expert_proofreading',
'ADD_VIP_SUPPORT' => 'vip_support',
'ADD_PLAGIARISM_REPORT' => 'plagiarism_report',
'ADD_DRAFT_OUTLINE' => 'draft_outline'
];
const ADD_SERVICES_ORDER = [
self::ADD_SERVICES_IDS['ADD_WRITING_PAGES'],
self::ADD_SERVICES_IDS['ADD_WRITING_SLIDES'],
self::ADD_SERVICES_IDS['ADD_WRITING_CHARTS'],
self::ADD_SERVICES_IDS['ADD_USED_SOURCES'],
self::ADD_SERVICES_IDS['ADD_PROVIDE_ME_SAMPLES'],
self::ADD_SERVICES_IDS['ADD_SHORTEN_DEADLINE'],
self::ADD_SERVICES_IDS['ADD_BOOST_CATEGORY'],
self::ADD_SERVICES_IDS['ADD_PROGRESSIVE_DELIVERY'],
self::ADD_SERVICES_IDS['ADD_EXPERT_PROOFREADING'],
self::ADD_SERVICES_IDS['ADD_VIP_SUPPORT'],
self::ADD_SERVICES_IDS['ADD_PLAGIARISM_REPORT'],
self::ADD_SERVICES_IDS['ADD_DRAFT_OUTLINE']
];
const UNMERGEABLE_ADD_SERVICES = [
self::ADD_SERVICES_IDS['ADD_SHORTEN_DEADLINE'],
self::ADD_SERVICES_IDS['ADD_BOOST_CATEGORY']
];
/**
* Approximates number to hundredth
* @param {number} amount
* @returns {number}
*/
public function normalizePrice( $amount ) {
return round($amount * 100) / 100;
}
/**
* Appends `s` to the word if number's last digit is not 1
* @param {string} word
* @param {number} number
* @returns {string}
*/
public function pluralize( $word, $number ) {
return $number == 1 ? $word : $word . 's';
}
/**
* Validates order values
* @param {OrderInfo=} order
* @returns {OrderInfo} valid order
*/
public function orderWithDefaults( $order ) {
if ( !$order ) $order = array();
$tariffPricePerPage = (int) ArrayUtils::get($order, 'tariffPricePerPage', 0);
$coupon = ArrayUtils::get($order, 'discount', null);
if ( $coupon ) {
if ($coupon['type_id'] == self::COUPONS_TYPES_IDS['PRICE_PER_PAGE']) {
$tariffPricePerPage = (float) $coupon['value'];
}
}
$defaults = array(
'pages' => (int) ArrayUtils::get($order, 'pages', 0),
'slides' => (int) ArrayUtils::get($order, 'slides', 0),
'discount' => $coupon,
'charts' => (int) ArrayUtils::get($order, 'charts', 0),
'winbackCoupons' => ArrayUtils::get($order, 'winbackCoupons', array()),
'writerCategoryId' => (int) ArrayUtils::get($order, 'writerCategoryId', null),
'tariffPricePerPage' => $tariffPricePerPage,
'tariffHrs' => (int) ArrayUtils::get($order, 'tariffHrs', 0),
'spacing' => ArrayUtils::get($order, 'spacing', null),
'getSamplesOn' => (bool) ArrayUtils::get($order, 'getSamplesOn'),
'getProgressiveDeliveryOn' => (bool) ArrayUtils::get($order, 'getProgressiveDeliveryOn'),
'getUsedSourcesOn' => (bool) ArrayUtils::get($order, 'getUsedSourcesOn'),
'complexAssignmentDiscipline' => (bool) ArrayUtils::get($order, 'complexAssignmentDiscipline'),
'writerPercent' => ArrayUtils::get($order, 'writerPercent'),
'expertProofreading' => (bool) ArrayUtils::get($order, 'expertProofreading'),
'vipSupport' => (bool) ArrayUtils::get($order, 'vipSupport'),
'plagiarismReport' => (bool) ArrayUtils::get($order, 'plagiarismReport'),
'draftOutline' => (bool) ArrayUtils::get($order, 'draftOutline')
);
return $defaults;
}
/**
* Validates additional services info
* @param {AddServicesInfo=} addServicesInfo
* @param {OrderInfo} originalOrder
* @returns {AddServicesInfo}
*/
public function addServicesInfoWithDefaults( $addServicesInfo, $originalOrder ) {
if (!ArrayUtils::get($addServicesInfo, 'newPagesQuantity')) {
$addServicesInfo['newPagesQuantity'] = ArrayUtils::get($originalOrder, 'pages');
}
if ( !ArrayUtils::get($addServicesInfo, 'newSlidesQuantity') ) {
$addServicesInfo['newSlidesQuantity'] = ArrayUtils::get($originalOrder, 'slides');
}
if (!ArrayUtils::get($addServicesInfo, 'newChartsQuantity')) {
$addServicesInfo['newChartsQuantity'] = ArrayUtils::get($originalOrder, 'charts');
}
if (!ArrayUtils::get($addServicesInfo, 'getUsedSourcesOn')) {
$addServicesInfo['getUsedSourcesOn'] = ArrayUtils::get($originalOrder, 'getUsedSourcesOn');
}
if (!ArrayUtils::get($addServicesInfo, 'getSamplesOn')) {
$addServicesInfo['getSamplesOn'] = ArrayUtils::get($originalOrder, 'getSamplesOn');
}
if (!ArrayUtils::get($addServicesInfo, 'expertProofreading')) {
$addServicesInfo['expertProofreading'] = ArrayUtils::get($originalOrder, 'expertProofreading');
}
if (!ArrayUtils::get($addServicesInfo, 'vipSupport')) {
$addServicesInfo['vipSupport'] = ArrayUtils::get($originalOrder, 'vipSupport');
}
if (!ArrayUtils::get($addServicesInfo, 'plagiarismReport')) {
$addServicesInfo['plagiarismReport'] = ArrayUtils::get($originalOrder, 'plagiarismReport');
}
if (!ArrayUtils::get($addServicesInfo, 'draftOutline')) {
$addServicesInfo['draftOutline'] = ArrayUtils::get($originalOrder, 'draftOutline');
}
if (!ArrayUtils::get($addServicesInfo, 'getProgressiveDeliveryOn')) {
$addServicesInfo['getProgressiveDeliveryOn'] = ArrayUtils::get($originalOrder, 'getProgressiveDeliveryOn');
}
if ( !ArrayUtils::get($addServicesInfo, 'shortenDeadlinePricePerPage') ) {
$addServicesInfo['shortenDeadlinePricePerPage'] = (int) ArrayUtils::get($originalOrder, 'tariffPricePerPage');
}
if ( !ArrayUtils::get($addServicesInfo, 'shortenDeadlineHrs') ) {
$addServicesInfo['shortenDeadlineHrs'] = (int) ArrayUtils::get($originalOrder, 'tariffHrs');
}
if (!ArrayUtils::get($addServicesInfo, 'boostWriterCategoryId')) {
$addServicesInfo['boostWriterCategoryId'] = ArrayUtils::get($originalOrder, 'writerCategoryId');
}
if ( !ArrayUtils::get($addServicesInfo, 'boostWriterPercent')) {
$addServicesInfo['boostWriterPercent'] = ArrayUtils::get($originalOrder, 'writerPercent');
}
return $addServicesInfo;
}
/**
* Transforms array of winback coupons to object that is easy to use in UI
* @param {Array} winbackCoupons
* @returns {{
* pages: number,
* slides: number,
* charts: number,
* progressiveDelivery: boolean,
* copyOfSources: boolean,
* writerSamples: boolean,
* categoriesOfWriter: Array.<number>
* }}
* @constructor
*/
public function getFreeThingsFromCoupons( $winbackCoupons ) {
if (!$winbackCoupons) { $winbackCoupons = []; }
$couponsMap = [];
$categoriesOfWriter = [];
for ($i = 0; $i < count($winbackCoupons); $i++) {
if ($winbackCoupons[$i]['service_type_id'] == self::SERVICES_IDS['CHOOSE_WRITER'] ) {
$categoriesOfWriter[] = $winbackCoupons[$i]['writer_category_id'];
} else {
$couponsMap[$winbackCoupons[$i]['service_type_id']] = $winbackCoupons[$i];
}
}
return array(
'pages' => $couponsMap[self::SERVICES_IDS['WRITING_PAGES']] &&
$couponsMap[self::SERVICES_IDS['WRITING_PAGES']]['quantity'] || 0,
'slides' => $couponsMap[self::SERVICES_IDS['WRITING_SLIDES']] &&
$couponsMap[self::SERVICES_IDS['WRITING_SLIDES']]['quantity'] || 0,
'charts' => $couponsMap[self::SERVICES_IDS['WRITING_CHARTS']] &&
$couponsMap[self::SERVICES_IDS['WRITING_CHARTS']]['quantity'] || 0,
'progressiveDelivery' => (bool) $couponsMap[self::SERVICES_IDS['PROGRESSIVE_DELIVERY']],
'copyOfSources' => (bool) $couponsMap[self::SERVICES_IDS['USED_SOURCES']],
'categoriesOfWriter' => $categoriesOfWriter,
'writerSamples' => (bool) $couponsMap[self::SERVICES_IDS['PROVIDE_ME_SAMPLES']],
);
}
/**
* Calculates base services
* @param {OrderInfo} order
* @returns {Object.<string, Service>} servicesById
*/
public function calculateBaseServices( $order ) {
$spacing_factor = ArrayUtils::get($order, 'spacing') == 'single' ? 2 : 1;
$pricePerPage = ArrayUtils::get($order, 'tariffPricePerPage') * $spacing_factor;
$pricePerSlide = $this->normalizePrice(ArrayUtils::get($order, 'tariffPricePerPage') * 0.5);
$pricePerChart = $this->normalizePrice(ArrayUtils::get($order, 'tariffPricePerPage') * 0.5);
$pages = (int) ArrayUtils::get($order, 'pages');
$slides = (int) ArrayUtils::get($order, 'slides');
$charts = (int) ArrayUtils::get($order, 'charts');
## pages
$basePagesCost = $this->normalizePrice($pages * $pricePerPage);
## slides
$baseSlidesCost = $this->normalizePrice($slides * $pricePerSlide);
## charts
$baseChartsCost = $this->normalizePrice($charts * $pricePerChart);
$writerCostPerPageType = Configuration::get('WRITER_CPP_TYPE', PROX_ADMIN_SITE_ID, "percentage");
if ($writerCostPerPageType == "fixed") {
$writerPricePerPage = (float) Configuration::get('WRITER_AMOUNT_CPP', PROX_ADMIN_SITE_ID, 0);
$writerPricePerPage = $writerPricePerPage * $spacing_factor;
$writerPricePerSlide = $this->normalizePrice($writerPricePerPage * 0.5);
$writerPricePerChart = $this->normalizePrice($writerPricePerPage * 0.5);
$writerPagesCost = $this->normalizePrice($pages * $writerPricePerPage);
$writerSlidesCost = $this->normalizePrice($slides * $writerPricePerSlide);
$writerChartsCost = $this->normalizePrice($charts * $writerPricePerChart);
} else {
$writerPercentageCpp = (float) Configuration::get('WRITER_PERCENTAGE_CPP', PROX_ADMIN_SITE_ID, 0);
$writerPricePerPage = $this->normalizePrice(
($pricePerPage * $writerPercentageCpp) / 100
);
$writerPricePerPage = $writerPricePerPage * $spacing_factor;
$writerPricePerSlide = $this->normalizePrice($writerPricePerPage * 0.5);
$writerPricePerChart = $this->normalizePrice($writerPricePerPage * 0.5);
$writerPagesCost = $this->normalizePrice($pages * $writerPricePerPage);
$writerSlidesCost = $this->normalizePrice($slides * $writerPricePerSlide);
$writerChartsCost = $this->normalizePrice($charts * $writerPricePerChart);
}
$editorCostPerPageType = Configuration::get('EDITOR_CPP_TYPE', PROX_ADMIN_SITE_ID, "percentage");
if ($editorCostPerPageType == "fixed") {
$editorPricePerPage = (float) Configuration::get('EDITOR_AMOUNT_CPP', PROX_ADMIN_SITE_ID, 0);
$editorPricePerPage = $editorPricePerPage * $spacing_factor;
$editorPricePerSlide = $this->normalizePrice($editorPricePerPage * 0.5);
$editorPricePerChart = $this->normalizePrice($editorPricePerPage * 0.5);
$editorPagesCost = $this->normalizePrice($pages * $editorPricePerPage);
$editorSlidesCost = $this->normalizePrice($slides * $editorPricePerSlide);
$editorChartsCost = $this->normalizePrice($charts * $editorPricePerChart);
} else {
$editorPercentageCpp = (float) Configuration::get('EDITOR_PERCENTAGE_CPP', PROX_ADMIN_SITE_ID, 0);
$editorPricePerPage = $this->normalizePrice(
($pricePerPage * $editorPercentageCpp) / 100
);
$editorPricePerPage = $editorPricePerPage * $spacing_factor;
$editorPricePerSlide = $this->normalizePrice($editorPricePerPage * 0.5);
$editorPricePerChart = $this->normalizePrice($editorPricePerPage * 0.5);
$editorPagesCost = $this->normalizePrice($pages * $editorPricePerPage);
$editorSlidesCost = $this->normalizePrice($slides * $editorPricePerSlide);
$editorChartsCost = $this->normalizePrice($charts * $editorPricePerChart);
}
$result = [];
$result[self::SERVICES_IDS['WRITING_PAGES']] = [
'cost' => $basePagesCost,
'writerCost' => $writerPagesCost,
'editorCost' => $editorPagesCost,
'winbackQuantity' => 0,
'winbackReduction' => 0,
'quantity' => ArrayUtils::get($order, 'pages'),
'itemPrice' => $pricePerPage,
'price' => 0,
'pricePercent' => 0,
];
$result[self::SERVICES_IDS['WRITING_SLIDES']] = [
'cost' => $baseSlidesCost,
'writerCost' => $writerSlidesCost,
'editorCost' => $editorSlidesCost,
'winbackQuantity' => 0,
'winbackReduction' => 0,
'quantity' => ArrayUtils::get($order, 'slides'),
'itemPrice' => $pricePerSlide,
'price' => 0,
'pricePercent' => 0,
];
$result[self::SERVICES_IDS['WRITING_CHARTS']] = [
'cost' => $baseChartsCost,
'writerCost' => $writerChartsCost,
'editorCost' => $editorChartsCost,
'winbackQuantity' => 0,
'winbackReduction' => 0,
'quantity' => ArrayUtils::get($order, 'charts'),
'itemPrice' => $pricePerChart,
'price' => 0,
'pricePercent' => 0,
];
return $result;
}
/**
* Calculates coupons service
* @param {OrderInfo} order
* @returns {Object.<string, Service>} servicesById
*/
public function applyBaseCoupons($order, $servicesById) {
$spacing_factor = (ArrayUtils::get($order, 'spacing') == 'single') ? 2 : 1;
for ($i = 0; $i < count(ArrayUtils::get($order, 'winbackCoupons')); $i++) {
$winbackCoupons = ArrayUtils::get($order, 'winbackCoupons');
$coupon = $winbackCoupons[$i];
if ( $coupon->type_id == self::COUPONS_TYPES_IDS['FREE_UNIT'] ) {
$service = $servicesById[$coupon->service_type_id];
if ($service) {
$quantity = min($coupon->quantity, $service->quantity - 1);
$quantity = max($quantity, 0);
$service['winbackQuantity'] = $quantity;
$service['winbackReduction'] = $this->normalizePrice($service['itemPrice'] * $quantity);
} else {
throw new Exception("Cant apply coupon");
}
}
}
return $servicesById;
}
/**
* Calculates secondary services
* @param {OrderInfo} order
* @param {number} baseCost
* @param {number} baseCouponsReduction
* @returns {Object.<string, Service>} servicesById
*/
public function calculateSecondaryServices($order, $baseCost, $baseCouponsReduction) {
$baseCostWithCoupons = $baseCost - $baseCouponsReduction;
/** Progressive Delivery **/
$pdDisabled = ($baseCost < 200 || ArrayUtils::get($order, 'tariffHrs') < 120)
? 'Available for orders with a deadline of 5 days and longer, and with the value of $200 and more.'
: null;
$pdForced = ($baseCost >= 600 && ArrayUtils::get($order, 'tariffHrs') >= 168)
? 'Mandatory for 7 days and longer, with the value of $600 and more.'
: null;
$progressiveDeliveryPrice = $pdDisabled
? 0
: $this->normalizePrice($baseCostWithCoupons * self::PROGRESSIVE_DELIVERY_PERCENT / 100);
$writerProgressiveDeliveryPercentage = (float) Configuration::get('WRITER_PROGRESSIVE_DELIVERY_COST', PROX_ADMIN_SITE_ID, 0);
$writerProgressiveDeliveryPrice = $this->normalizePrice(
($progressiveDeliveryPrice * $writerProgressiveDeliveryPercentage) / 100
);
$editorProgressiveDeliveryPercentage = (float) Configuration::get('EDITOR_PROGRESSIVE_DELIVERY_COST', PROX_ADMIN_SITE_ID, 0);
$editorProgressiveDeliveryPrice = $this->normalizePrice(
($progressiveDeliveryPrice * $editorProgressiveDeliveryPercentage) / 100
);
/** Used Sources **/
$usedSourcesPrice = $this->normalizePrice(
max( self::USED_SOURCES_MIN_PRICE, $baseCostWithCoupons * self::USED_SOURCES_PERCENT / 100)
);
$writerUsedSourcesPercentage = (float) Configuration::get('WRITER_USED_SOURCES_COST', PROX_ADMIN_SITE_ID, 0);
$writerUsedSourcesPrice = $this->normalizePrice(
($usedSourcesPrice * $writerUsedSourcesPercentage) / 100
);
$editorUsedSourcesPercentage = (float) Configuration::get('EDITOR_USED_SOURCES_COST', PROX_ADMIN_SITE_ID, 0);
$editorUsedSourcesPrice = $this->normalizePrice(
($usedSourcesPrice * $editorUsedSourcesPercentage) / 100
);
/** Complex Assignment **/
$complexAssignmentPrice = $this->normalizePrice(
($baseCostWithCoupons * self::COMPLEX_ASSIGNMENT_PERCENT) / 100
);
$writerComplexAssignmentPercentage = (float) Configuration::get('WRITER_COMPLEX_ASSIGNMENT_COST', PROX_ADMIN_SITE_ID, 0);
$writerComplexAssignmentPrice = $this->normalizePrice(
($complexAssignmentPrice * $writerComplexAssignmentPercentage) / 100
);
$editorComplexAssignmentPercentage = (float) Configuration::get('EDITOR_COMPLEX_ASSIGNMENT_COST', PROX_ADMIN_SITE_ID, 0);
$editorComplexAssignmentPrice = $this->normalizePrice(
($complexAssignmentPrice * $editorComplexAssignmentPercentage) / 100
);
/** Expert Proofreading **/
$expertProofreadingPrice = $this->normalizePrice(
($baseCostWithCoupons * self::EXPERT_PROOFREADING_PERCENT) / 100
);
$writerExpertProofreadingPercentage = (float) Configuration::get('WRITER_EXPERT_PROOFREADING_COST', PROX_ADMIN_SITE_ID, 0);
$writerExpertProofreadingPrice = $this->normalizePrice(
($expertProofreadingPrice * $writerExpertProofreadingPercentage) / 100
);
$editorExpertProofreadingPercentage = (float) Configuration::get('EDITOR_EXPERT_PROOFREADING_COST', PROX_ADMIN_SITE_ID, 0);
$editorExpertProofreadingPrice = $this->normalizePrice(
($expertProofreadingPrice * $editorExpertProofreadingPercentage) / 100
);
/** Draft Outline **/
$draftOutlinePrice = $this->normalizePrice(
($baseCostWithCoupons * self::DRAFT_OUTLINE_PERCENT) / 100
);
$writerDraftOutlinePrice = (float) Configuration::get('WRITER_DRAFT_OUTLINE_COST', PROX_ADMIN_SITE_ID, 0);
$writerDraftOutlinePrice = $this->normalizePrice(
($draftOutlinePrice * $writerDraftOutlinePrice) / 100
);
$editorDraftOutlinePrice = (float) Configuration::get('EDITOR_DRAFT_OUTLINE_COST', PROX_ADMIN_SITE_ID, 0);
$editorDraftOutlinePrice = $this->normalizePrice(
($draftOutlinePrice * $editorDraftOutlinePrice) / 100
);
/** Writer Samples **/
$getSamplesPrice = self::PROVIDE_SAMPLES_PRICE;
$writerGetSamplesPercentage = (float) Configuration::get('WRITER_PROVIDE_ME_SAMPLES_COST', PROX_ADMIN_SITE_ID, 0);
$writerGetSamplesPrice = $this->normalizePrice(
($getSamplesPrice * $writerGetSamplesPercentage) / 100
);
$editorGetSamplesPercentage = (float) Configuration::get('EDITOR_PROVIDE_ME_SAMPLES_COST', PROX_ADMIN_SITE_ID, 0);
$editorGetSamplesPrice = $this->normalizePrice(
($getSamplesPrice * $editorGetSamplesPercentage) / 100
);
/** VIP Support **/
$vipSupportPrice = self::VIP_SUPPORT_PRICE;
$writerVipSupportPercentage = (float) Configuration::get('WRITER_VIP_SUPPORT_COST', PROX_ADMIN_SITE_ID, 0);
$writerVipSupportPrice = $this->normalizePrice(
($vipSupportPrice * $writerVipSupportPercentage) / 100
);
$editorVipSupportPercentage = (float) Configuration::get('EDITOR_VIP_SUPPORT_COST', PROX_ADMIN_SITE_ID, 0);
$editorVipSupportPrice = $this->normalizePrice(
($vipSupportPrice * $editorVipSupportPercentage) / 100
);
/** Plagiarism Report **/
$plagiarismReportPrice = self::PLAGIARISM_REPORT_PRICE;
$writerPlagiarismReportPercentage = (float) Configuration::get('WRITER_PLAGIARISM_REPORT_COST', PROX_ADMIN_SITE_ID, 0);
$writerPlagiarismReportPrice = $this->normalizePrice(
($plagiarismReportPrice * $writerPlagiarismReportPercentage) / 100
);
$editorPlagiarismReportPercentage = (float) Configuration::get('EDITOR_PLAGIARISM_REPORT_COST', PROX_ADMIN_SITE_ID, 0);
$editorPlagiarismReportPrice = $this->normalizePrice(
($plagiarismReportPrice * $editorPlagiarismReportPercentage) / 100
);
/** Writer Percent **/
$writerPercentPrice = $this->normalizePrice($baseCostWithCoupons * ArrayUtils::get($order, 'writerPercent') / 100);
$writerCategoryPricePercentage = (float) Configuration::get('WRITER_W_CATEGORY_COST', PROX_ADMIN_SITE_ID, 0);
$writerCategoryPrice = $this->normalizePrice(
($writerPercentPrice * $writerCategoryPricePercentage) / 100
);
$editorCategoryPricePercentage = (float) Configuration::get('EDITOR_W_CATEGORY_COST', PROX_ADMIN_SITE_ID, 0);
$editorCategoryPrice = $this->normalizePrice(
($writerPercentPrice * $editorCategoryPricePercentage) / 100
);
$result = [];
$result[self::SERVICES_IDS['PROVIDE_ME_SAMPLES']] = [
'cost' => ArrayUtils::get($order, 'getSamplesOn') ? $getSamplesPrice : 0,
'writerCost' => ArrayUtils::get($order, 'getSamplesOn') ? $writerGetSamplesPrice : 0,
'editorCost' => ArrayUtils::get($order, 'getSamplesOn') ? $editorGetSamplesPrice : 0,
'winbackQuantity' => 0,
'winbackReduction' => 0,
'quantity' => 0,
'itemPrice' => 0,
'price' => $getSamplesPrice,
'pricePercent' => 0,
];
$result[self::SERVICES_IDS['PROGRESSIVE_DELIVERY']] = [
'cost' => ($pdForced || ArrayUtils::get($order, 'getProgressiveDeliveryOn')) ? $progressiveDeliveryPrice : 0,
'writerCost' => ($pdForced || ArrayUtils::get($order, 'getProgressiveDeliveryOn')) ? $writerProgressiveDeliveryPrice : 0,
'editorCost' => ($pdForced || ArrayUtils::get($order, 'getProgressiveDeliveryOn')) ? $editorProgressiveDeliveryPrice : 0,
'winbackQuantity' => 0,
'winbackReduction' => 0,
'quantity' => 0,
'itemPrice' => 0,
'price' => $progressiveDeliveryPrice,
'pricePercent' => self::PROGRESSIVE_DELIVERY_PERCENT,
'disabledReason' => $pdDisabled,
'forcedReason' => $pdForced,
];
$result[self::SERVICES_IDS['CHOOSE_WRITER']] = [
'cost' => $writerPercentPrice,
'writerCost' => $writerCategoryPrice,
'editorCost' => 0,
'winbackQuantity' => 0,
'winbackReduction' => 0,
'quantity' => 0,
];
$result[self::SERVICES_IDS['USED_SOURCES']] = [
'cost' => ArrayUtils::get($order, 'getUsedSourcesOn') ? $usedSourcesPrice : 0,
'writerCost' => ArrayUtils::get($order, 'getUsedSourcesOn') ? $writerUsedSourcesPrice : 0,
'editorCost' => ArrayUtils::get($order, 'getUsedSourcesOn') ? $editorUsedSourcesPrice : 0,
'winbackQuantity' => 0,
'winbackReduction' => 0,
'quantity' => 0,
'itemPrice' => 0,
'price' => $usedSourcesPrice,
'pricePercent' => $usedSourcesPrice > self::USED_SOURCES_MIN_PRICE ? self::USED_SOURCES_PERCENT : 0,
];
$result[self::SERVICES_IDS['COMPLEX_ASSIGNMENT']] = [
'cost' => ArrayUtils::get($order, 'complexAssignmentDiscipline') ? $complexAssignmentPrice : 0,
'writerCost' => ArrayUtils::get($order, 'complexAssignmentDiscipline') ? $writerComplexAssignmentPrice : 0,
'editorCost' => ArrayUtils::get($order, 'complexAssignmentDiscipline') ? $editorComplexAssignmentPrice : 0,
'winbackQuantity' => 0,
'winbackReduction' => 0,
'quantity' => 0,
'itemPrice' => 0,
'price' => $complexAssignmentPrice,
'pricePercent' => self::COMPLEX_ASSIGNMENT_PERCENT,
];
$result[self::SERVICES_IDS['EXPERT_PROOFREADING']] = [
'cost' => ArrayUtils::get($order, 'expertProofreading') ? $expertProofreadingPrice : 0,
'writerCost' => ArrayUtils::get($order, 'expertProofreading') ? $writerExpertProofreadingPrice : 0,
'editorCost' => ArrayUtils::get($order, 'expertProofreading') ? $editorExpertProofreadingPrice : 0,
'winbackQuantity' => 0,
'winbackReduction' => 0,
'quantity' => 0,
'itemPrice' => 0,
'price' => $expertProofreadingPrice,
'pricePercent' => self::EXPERT_PROOFREADING_PERCENT,
];
$result[self::SERVICES_IDS['VIP_SUPPORT']] = [
'cost' => ArrayUtils::get($order, 'vipSupport') ? $vipSupportPrice : 0,
'writerCost' => ArrayUtils::get($order, 'vipSupport') ? $writerVipSupportPrice : 0,
'editorCost' => ArrayUtils::get($order, 'vipSupport') ? $editorVipSupportPrice : 0,
'winbackQuantity' => 0,
'winbackReduction' => 0,
'quantity' => 0,
'itemPrice' => 0,
'price' => $vipSupportPrice,
'pricePercent' => 0
];
$result[self::SERVICES_IDS['PLAGIARISM_REPORT']] = [
'cost' => ArrayUtils::get($order, 'plagiarismReport') ? $plagiarismReportPrice : 0,
'writerCost' => ArrayUtils::get($order, 'plagiarismReport') ? $writerPlagiarismReportPrice : 0,
'editorCost' => ArrayUtils::get($order, 'plagiarismReport') ? $editorPlagiarismReportPrice : 0,
'winbackQuantity' => 0,
'winbackReduction' => 0,
'quantity' => 0,
'itemPrice' => 0,
'price' => $plagiarismReportPrice,
'pricePercent' => 0,
];
$result[self::SERVICES_IDS['DRAFT_OUTLINE']] = [
'cost' => ArrayUtils::get($order, 'draftOutline') ? $draftOutlinePrice : 0,
'writerCost' => ArrayUtils::get($order, 'draftOutline') ? $writerDraftOutlinePrice : 0,
'editorCost' => ArrayUtils::get($order, 'draftOutline') ? $editorDraftOutlinePrice : 0,
'winbackQuantity' => 0,
'winbackReduction' => 0,
'quantity' => 0,
'itemPrice' => 0,
'price' => $draftOutlinePrice,
'pricePercent' => self::DRAFT_OUTLINE_PERCENT,
];
/**** Secondary coupons ****/
for ($i = 0; $i < count(ArrayUtils::get($order, 'winbackCoupons')); $i++) {
$winbackCoupons = ArrayUtils::get($order, 'winbackCoupons');
$coupon = $winbackCoupons[$i];
if ($coupon['type_id'] == self::COUPONS_TYPES_IDS['PERCENT'] ) {
$service = $result[ $coupon['service_type_id'] ];
$result[$coupon['service_type_id']]['winbackReduction'] = $this->normalizePrice($result[$coupon['service_type_id']]['cost'] / 100 * $coupon['value'] );
} else if ($coupon['type_id'] == self::COUPONS_TYPES_IDS['WRITER_CATEGORY'] ) {
if (ArrayUtils::get($order, 'writerCategoryId') == $coupon['writer_category_id'] ) {
$result[self::SERVICES_IDS['CHOOSE_WRITER']]['winbackReduction'] = $result[self::SERVICES_IDS['CHOOSE_WRITER']]['cost'];
}
}
}
return $result;
}
/**
* @param {OrderInfo} order
* @param {number} rawCost
* @param {number} couponsReduction
* @returns {Object.<string, Service>} servicesById
*/
public function calculateDiscountServices( $order, $rawCost, $couponsReduction ) {
$result = [];
$coupon = ArrayUtils::get($order, 'discount');
if (!is_array($coupon)) return [];
$discount = 0;
if ( $coupon['type_id'] == self::COUPONS_TYPES_IDS['AMOUNT'] ) {
$discount = -$coupon['value'];
} else {
$discount = -$this->normalizePrice(($rawCost - $couponsReduction) * $coupon['value'] / 100);
}
$result[ self::SERVICES_IDS['DISCOUNT'] ] = [
'cost' => $discount,
'writerCost' => 0,
'editorCost' => 0,
'winbackQuantity' => 0,
'winbackReduction' => 0,
'quantity' => 0
];
return $result;
}
public function formatQuantitiveOperation( $operation, $itemName, $skipSign = true ) {
$operations = ArrayUtils::get($operation, 'operations');
if ( is_array($operations) ) {
$formattedOperations = [];
for ($i = 0; $i < count($operations); $i++) {
$subOperation = ArrayUtils::get($operations, $i);
$formattedOperations[] = $this->formatQuantitiveOperation($subOperation, $itemName, $i === 0);
}
return implode(' ', $formattedOperations);
}
$quantity = ArrayUtils::get($operation, 'quantity');
$itemPrice = ArrayUtils::get($operation, 'itemPrice');
$sign = ArrayUtils::get($operation, 'sign');
$formatted = $quantity . ' ' . $this->pluralize($itemName, $quantity) . ' × $' . $this->normalizePrice($itemPrice);
if ($skipSign) return $formatted;
if ($sign < 0) return '− ' . $formatted;
if ($sign > 0) return '+ ' . $formatted;
return $formatted;
}
/**
* Returns title for given service & service id
* @param {Service} service
* @param {number|string} serviceId
* @returns {string} Title
*/
public function getServiceTitle( $service, $serviceId ) {
switch ($serviceId) {
case self::SERVICES_IDS['PROVIDE_ME_SAMPLES']:
return 'Order writer’s samples';
break;
case self::SERVICES_IDS['EXPERT_PROOFREADING']:
return 'Expert Proofreading';
break;
case self::SERVICES_IDS['VIP_SUPPORT']:
return 'VIP Support';
break;
case self::SERVICES_IDS['PLAGIARISM_REPORT']:
return 'Plagiarism Report';
break;
case self::SERVICES_IDS['DRAFT_OUTLINE']:
return 'Draft/Outline';
break;
case self::SERVICES_IDS['PROGRESSIVE_DELIVERY']:
return 'Progressive delivery';
break;
case self::SERVICES_IDS['WRITING_PAGES']:
return $this->formatQuantitiveOperation($service, 'page');
break;
case self::SERVICES_IDS['WRITING_SLIDES']:
return $this->formatQuantitiveOperation($service, 'PowerPoint slide');
break;
case self::SERVICES_IDS['WRITING_CHARTS']:
return $this->formatQuantitiveOperation($service, 'chart');
break;
case self::SERVICES_IDS['CHOOSE_WRITER']:
return 'Category of the writer';
break;
case self::SERVICES_IDS['COMPLEX_ASSIGNMENT']:
return 'Complex assignment';
break;
case self::SERVICES_IDS['DISCOUNT']:
return 'Discount';
break;
case self::SERVICES_IDS['USED_SOURCES']:
return 'Copy of sources used';
break;
case self::ADD_SERVICES_IDS['ADD_BOOST_CATEGORY']:
return 'Boost category';
break;
case self::ADD_SERVICES_IDS['ADD_SHORTEN_DEADLINE']:
return 'Shortening of the deadline';
break;
default:
return $serviceId;
}
}
/**
* Returns title for given service & service id
* @param {number|string} serviceId
* @returns {number} Priority
*/
public function getServicePriority($serviceId) {
switch ($serviceId) {
case self::SERVICES_IDS['WRITING_PAGES'] :
return 1;
break;
case self::SERVICES_IDS['WRITING_SLIDES'] :
return 2;
break;
case self::SERVICES_IDS['WRITING_CHARTS'] :
return 3;
break;
case self::SERVICES_IDS['PROGRESSIVE_DELIVERY'] :
return 4;
break;
case self::SERVICES_IDS['CHOOSE_WRITER'] :
return 5;
break;
case self::SERVICES_IDS['USED_SOURCES'] :
return 6;
break;
case self::SERVICES_IDS['COMPLEX_ASSIGNMENT'] :
return 7;
break;
case self::SERVICES_IDS['PROVIDE_ME_SAMPLES'] :
return 8;
break;
case self::SERVICES_IDS['ADD_BOOST_CATEGORY']:
return 10;
break;
case self::SERVICES_IDS['ADD_SHORTEN_DEADLINE']:
return 11;
break;
case self::SERVICES_IDS['EXPERT_PROOFREADING'] :
return 12;
break;
case self::SERVICES_IDS['VIP_SUPPORT'] :
return 13;
break;
case self::SERVICES_IDS['PLAGIARISM_REPORT'] :
return 14;
break;
case self::SERVICES_IDS['DRAFT_OUTLINE'] :
return 15;
break;
case self::ADD_SERVICES_IDS['ADD_WRITING_PAGES'] :
return 50;
break;
case self::ADD_SERVICES_IDS['ADD_WRITING_SLIDES'] :
return 51;
break;
case self::ADD_SERVICES_IDS['ADD_WRITING_CHARTS'] :
return 52;
break;
case self::ADD_SERVICES_IDS['ADD_USED_SOURCES'] :
return 53;
break;
case self::ADD_SERVICES_IDS['ADD_PROVIDE_ME_SAMPLES'] :
return 54;
break;
case self::ADD_SERVICES_IDS['ADD_PROGRESSIVE_DELIVERY'] :
return 55;
break;
case self::ADD_SERVICES_IDS['ADD_SHORTEN_DEADLINE'] :
return 56;
break;
case self::ADD_SERVICES_IDS['ADD_BOOST_CATEGORY'] :
return 57;
break;
case self::ADD_SERVICES_IDS['ADD_EXPERT_PROOFREADING'] :
return 59;
break;
case self::SERVICES_IDS['DISCOUNT'] :
return 100;
break;
default :
return 0;
break;
}
}
/**
* @param {Object.<string, Service>} servicesById Base services
* @returns {number} Base cost
*/
public function getBaseCost( $servicesById ) {
return $this->normalizePrice(
$servicesById[ self::SERVICES_IDS['WRITING_PAGES'] ]['cost'] +
$servicesById[ self::SERVICES_IDS['WRITING_SLIDES'] ]['cost'] +
$servicesById[ self::SERVICES_IDS['WRITING_CHARTS'] ]['cost']
);
}
/**
* @param {Object.<string, Service>} servicesById Base+base coupons services
* @returns {number} Base cost
*/
public function getBaseCouponsReduction( $servicesById ) {
return $this->normalizePrice(
$servicesById[ self::SERVICES_IDS['WRITING_PAGES'] ]['winbackReduction'] +
$servicesById[ self::SERVICES_IDS['WRITING_SLIDES'] ]['winbackReduction'] +
$servicesById[ self::SERVICES_IDS['WRITING_CHARTS'] ]['winbackReduction']
);
}
/**
* @param {Object.<string, Service>} servicesById Base+secondary services
* @returns {number} Secondary cost
*/
public function getSecondaryCost( $servicesById ) {
return $this->normalizePrice(
$servicesById[ self::SERVICES_IDS['USED_SOURCES'] ]['cost'] +
$servicesById[ self::SERVICES_IDS['PROVIDE_ME_SAMPLES'] ]['cost'] +
$servicesById[ self::SERVICES_IDS['PROGRESSIVE_DELIVERY'] ]['cost'] +
$servicesById[ self::SERVICES_IDS['CHOOSE_WRITER'] ]['cost'] +
$servicesById[ self::SERVICES_IDS['COMPLEX_ASSIGNMENT'] ]['cost'] +
$servicesById[ self::SERVICES_IDS['EXPERT_PROOFREADING'] ]['cost'] +
$servicesById[ self::SERVICES_IDS['VIP_SUPPORT'] ]['cost'] +
$servicesById[ self::SERVICES_IDS['PLAGIARISM_REPORT'] ]['cost'] +
$servicesById[ self::SERVICES_IDS['DRAFT_OUTLINE'] ]['cost']
);
}
/**
* @param {Object.<string, Service>} servicesById any set of services
* @returns {number} Total cost
*/
public function getTotalCost( $servicesById ) {
$totalCost = 0;
foreach ( $servicesById as $serviceId => $service ) {
$totalCost = $this->normalizePrice($totalCost + $service['cost']);
}
return $totalCost;
}
/**
* @param {Object.<string, Service>} servicesById any set of services
* @returns {number} Writer cost
*/
public function getWriterTotalCost( $servicesById ) {
$totalCost = 0;
foreach ( $servicesById as $serviceId => $service ) {
$totalCost = $this->normalizePrice($totalCost + $service['writerCost'] );
}
return $totalCost;
}
/**
* @param {Object.<string, Service>} servicesById any set of services
* @returns {number} Editor cost
*/
public function getEditorTotalCost( $servicesById ) {
$totalCost = 0;
foreach ( $servicesById as $serviceId => $service ) {
$totalCost = $this->normalizePrice($totalCost + $service['editorCost'] - $service['winbackReduction'] );
}
return $totalCost;
}
/**
* @param {Object.<string, Service>} servicesById Base+secondary services
* @returns {number} Winback coupons reduction
*/
public function getCouponsReduction( $servicesById ) {
return $this->normalizePrice(
$this->getBaseCouponsReduction( $servicesById ) +
$servicesById[ self::SERVICES_IDS['PROVIDE_ME_SAMPLES'] ]['winbackReduction'] +
$servicesById[ self::SERVICES_IDS['PROGRESSIVE_DELIVERY'] ]['winbackReduction'] +
$servicesById[ self::SERVICES_IDS['USED_SOURCES'] ]['winbackReduction'] +
$servicesById[ self::SERVICES_IDS['CHOOSE_WRITER'] ]['winbackReduction'] +
$servicesById[ self::SERVICES_IDS['COMPLEX_ASSIGNMENT'] ]['winbackReduction'] +
$servicesById[ self::SERVICES_IDS['EXPERT_PROOFREADING'] ]['winbackReduction'] +
$servicesById[ self::SERVICES_IDS['VIP_SUPPORT'] ]['winbackReduction'] +
$servicesById[ self::SERVICES_IDS['PLAGIARISM_REPORT'] ]['winbackReduction'] +
$servicesById[ self::SERVICES_IDS['DRAFT_OUTLINE'] ]['winbackReduction']
);
}
/**
* @param {Object.<string, Service>} servicesById services
* @returns {number} Virtual services cost cost
*/
public function getAdditionalServicesRawCost( $servicesById ) {
$result = 0;
foreach ( self::ADD_SERVICES_IDS as $p ) {
$result = $this->normalizePrice($result + $servicesById[self::ADD_SERVICES_IDS[$p]]['rawCost'] );
}
return $result;
}
/**
* Returns complete set of services for order
* @param {OrderInfo} order
* @returns {Object.<string, Service>} servicesById
*/
public function calculateServices( $order ) {
$order = $this->orderWithDefaults( $order );
$baseServices = $this->calculateBaseServices($order);
$servicesById = $baseServices;
$baseCost = $this->getBaseCost( $servicesById );
$baseCouponsReduction = $this->getBaseCouponsReduction( $servicesById );
$secondaryServices = $this->calculateSecondaryServices($order, $baseCost, $baseCouponsReduction);
$servicesById = $secondaryServices + $servicesById;
$secondaryCost = $this->getSecondaryCost($servicesById);
$rawCost = $this->normalizePrice($baseCost + $secondaryCost);
$couponsReduction = $this->getCouponsReduction($servicesById);
$discountServices = $this->calculateDiscountServices($order, $rawCost, $couponsReduction);
$servicesById = $servicesById + $discountServices;
return $this->formatServicesById($servicesById);
}
/**
* Formats calculator's output for the end user
* @param {Object.<string, Service>} servicesById
* @returns {Object.<string, Service>} Services map
*/
public function formatServicesById( $servicesById ) {
foreach( $servicesById as $serviceId => $service ) {
$isFormatted = (bool) ArrayUtils::get($service, 'isFormatted');
if ( $isFormatted ) {
continue;
} else {
$service['isFormatted'] = true;
}
$service['winbackReduction'] = $service['winbackReduction'] || 0;
$service['title'] = $this->getServiceTitle( $service, $serviceId);
$service['free'] = $service['cost'] == $service['winbackReduction'] && $service['cost'] > 0;
$service['type_id'] = $serviceId;
$service['priority'] = $this->getServicePriority( $serviceId );
$service['active'] = $service['cost'] != 0 || $service['winbackReduction'] != 0;
$servicesById[$serviceId] = $service;
}
return $servicesById;
}
/**
* Basic orderform calculator
* @param {OrderInfo} order
* @returns {CostInfo}
*/
public function calculate( $order ) {
$order = $this->orderWithDefaults( $order );
$servicesById = $this->calculateServices( $order );
$output = array(
'servicesById' => $servicesById
);
$output['baseCost'] = $this->getBaseCost( $servicesById );
$output['secondaryCost'] = $this->getSecondaryCost( $servicesById );
$output['rawCost'] = $this->normalizePrice($output['baseCost'] + $output['secondaryCost']);
$output['couponsReduction'] = $this->getCouponsReduction( $servicesById);
$output['totalCost'] = $this->getTotalCost( $servicesById );
$output['writerCost'] = $this->getWriterTotalCost( $servicesById );
$output['editorCost'] = $this->getEditorTotalCost( $servicesById );
return $output;
}
public function getAddServiceOrderInfoPatch($addServicesInfo, $type_id) {
switch($type_id) {
case self::ADD_SERVICES_IDS['ADD_SHORTEN_DEADLINE']:
return [
'tariffHrs' => $addServicesInfo['shortenDeadlineHrs'],
'tariffPricePerPage' => $addServicesInfo['shortenDeadlinePricePerPage'],
];
break;
case self::ADD_SERVICES_IDS['ADD_WRITING_PAGES']:
return [
'pages' => $addServicesInfo['newPagesQuantity']
];
break;
case self::ADD_SERVICES_IDS['ADD_WRITING_SLIDES']:
return [
'slides' => $addServicesInfo['newSlidesQuantity']
];
break;
case self::ADD_SERVICES_IDS['ADD_WRITING_CHARTS']:
return [
'charts' => $addServicesInfo['newChartsQuantity']
];
break;
case self::ADD_SERVICES_IDS['ADD_USED_SOURCES']:
return [
'getUsedSourcesOn' => $addServicesInfo['getUsedSourcesOn']
];
break;
case self::ADD_SERVICES_IDS['ADD_PROVIDE_ME_SAMPLES']:
return [
'getSamplesOn' => $addServicesInfo['getSamplesOn'],
];
break;
case self::ADD_SERVICES_IDS['ADD_EXPERT_PROOFREADING']:
return [
'expertProofreading' => $addServicesInfo['expertProofreading'],
];
break;
case self::ADD_SERVICES_IDS['ADD_VIP_SUPPORT']:
return [
'vipSupport' => $addServicesInfo['vipSupport'],
];
break;
case self::ADD_SERVICES_IDS['ADD_DRAFT_OUTLINE']:
return [
'draftOutline' => $addServicesInfo['draftOutline'],
];
break;
case self::ADD_SERVICES_IDS['ADD_PLAGIARISM_REPORT']:
return [
'plagiarismReport' => $addServicesInfo['plagiarismReport'],
];
break;
case self::ADD_SERVICES_IDS['ADD_PROGRESSIVE_DELIVERY']:
return [
'getProgressiveDeliveryOn' => $addServicesInfo['getProgressiveDeliveryOn']
];
break;
case self::ADD_SERVICES_IDS['ADD_BOOST_CATEGORY']:
return [
'writerCategoryId' => $addServicesInfo['boostWriterCategoryId'],
'writerPercent' => $addServicesInfo['boostWriterPercent'],
];
break;
}
}
public function calculateAddService($addServicesInfo, $mutableOrder, $type_id) {
$patch = $this->getAddServiceOrderInfoPatch($addServicesInfo, $type_id);
$originalServices = $this->calculateServices( $mutableOrder );
$mutableOrder = array_merge($mutableOrder, $patch);
$nextServices = $this->calculateServices( $mutableOrder );
$originalRawCost = $this->normalizePrice( $this->getBaseCost($originalServices) + $this->getSecondaryCost($originalServices));
$nextRawCost = $this->normalizePrice( $this->getBaseCost($nextServices) + $this->getSecondaryCost($nextServices));
$originalCost = $this->normalizePrice( $this->getTotalCost($originalServices));
$nextCost = $this->normalizePrice( $this->getTotalCost($nextServices));
$servicesDiff = $this->subtractServices($nextServices, $originalServices, $type_id);
$result = array(
'rawCost' => $this->normalizePrice($nextRawCost - $originalRawCost),
'cost' => $this->normalizePrice($nextCost - $originalCost),
'winbackQuantity' => 0,
'winbackReduction' => 0,
'underlyingServicesById' => $this->formatServicesById($servicesDiff)
);
return $result;
}
/**
* Subtracts values of cost, winbackReduction and quantity from the minuend map of services
* @param {Object} nextServices - minuend
* @param {Object} prevServices - subtrahend
* @returns {Object.<string, Service>} Services map
*/
public function subtractServices($nextServices, $prevServices, $causedByServiceId) {
$finalServices = array();
foreach( $nextServices as $serviceId => $nextService ) {
$finalService = [];
if ( ArrayUtils::has($prevServices, $serviceId) ) {
$prevService = ArrayUtils::get($prevServices, $serviceId);
$finalService['cost'] = $this->normalizePrice( $nextService['cost'] - $prevService['cost'] );
$finalService['quantity'] = $nextService['quantity'] - $prevService['quantity'];
$finalService['winbackQuantity'] = $nextService['winbackQuantity'] - $prevService['winbackQuantity'];
$finalService['winbackReduction'] = $this->normalizePrice( $nextService['winbackReduction'] - $prevService['winbackReduction'] );
// Useless metrics, just copy the later values (for consistency) (use $service['operations)
// Todo: remove
if (ArrayUtils::has($nextService, 'price')) {
$finalService['price'] = $nextService['price'];
}
if (ArrayUtils::has($nextService, 'itemPrice')) {
$finalService['itemPrice'] = $nextService['itemPrice'];
}
if (ArrayUtils::has($nextService, 'pricePercent')) {
$finalService['pricePercent'] = $nextService['pricePercent'];
}
if (ArrayUtils::has($nextService, 'disabledReason')) {
$finalService['disabledReason'] = $nextService['disabledReason'];
}
if (ArrayUtils::has($nextService, 'forcedReason')) {
$finalService['forcedReason'] = $nextService['forcedReason'];
}
if( !ArrayUtils::has($finalService, 'operations') ) {
$finalService['operations'] = array();
}
if (
ArrayUtils::get($nextService, 'quantity') != ArrayUtils::get($prevService, 'quantity') ||
ArrayUtils::get($nextService, 'price') != ArrayUtils::get($prevService, 'price') ||
ArrayUtils::get($nextService, 'itemPrice') != ArrayUtils::get($prevService, 'itemPrice') ||
ArrayUtils::get($nextService, 'pricePercent') != ArrayUtils::get($prevService, 'pricePercent')
) {
// Quantitative information is differs, calculate operations
if (
ArrayUtils::get($nextService, 'price') == ArrayUtils::get($prevService, 'price') ||
ArrayUtils::get($nextService, 'itemPrice') == ArrayUtils::get($prevService, 'itemPrice') ||
ArrayUtils::get($nextService, 'pricePercent') == ArrayUtils::get($prevService, 'pricePercent')
) {
// Only quantity is different
$finalService['operations'][] = array(
'sign' => 1,
'quantity' => $nextService['quantity'] - $prevService['quantity'],
'price' => $nextService['price'],
'itemPrice' => $nextService['itemPrice'],
'pricePercent' => $nextService['pricePercent'],
'causedByServiceId' => $causedByServiceId,
);
} else {
$finalService['operations'][] = array(
[
'sign' => 1,
'quantity' => $nextService['quantity'],
'price' => $nextService['price'],
'itemPrice' => $nextService['itemPrice'],
'pricePercent' => $nextService['pricePercent'],
'causedByServiceId' => $causedByServiceId,
],
[
'sign' => -1,
'quantity' => $prevService['quantity'],
'price' => $prevService['price'],
'itemPrice' => $prevService['itemPrice'],
'pricePercent' => $prevService['pricePercent'],
'causedByServiceId' => $causedByServiceId,
]
);
}
}
}
$finalServices[$serviceId] = $finalService;
}
return $finalServices;
}
public function mergeServices($aServices, $bServices) {
$mergedServicesById = [];
foreach($aServices as $serviceId) {
$aService = ArrayUtils::get($aServices, $serviceId);
$bService = ArrayUtils::get($bServices, $serviceId);
$mergedService = $aService;
if ( ArrayUtils::has($aService, 'operations') ) {
$mergedService['operations'] = ArrayUtils::get($aService, 'operations');
}
if ($bService) {
$mergedService['quantity'] = $mergedService['quantity'] + $bService['quantity'];
$mergedService['cost'] = $this->normalizePrice($mergedService['cost'] + $bService['cost']);
$mergedService['winbackQuantity'] = $mergedService['winbackQuantity'] + $bService['winbackQuantity'];
$mergedService['winbackReduction'] = $this->normalizePrice($mergedService['winbackReduction'] + $bService['winbackReduction'] );
// Useless metrics, just copy the later values (for consistency) (use $service['operations)
if(ArrayUtils::has($bService, 'price')) $mergedService['price'] = $bService['price'];
if(ArrayUtils::has($bService, 'itemPrice')) $mergedService['itemPrice'] = $bService['itemPrice'];
if(ArrayUtils::has($bService, 'pricePercent')) $mergedService['pricePercent'] = $bService['pricePercent'];
if(ArrayUtils::has($bService, 'disabledReason')) $mergedService['disabledReason'] = $bService['disabledReason'];
if(ArrayUtils::has($bService, 'forcedReason')) $mergedService['forcedReason'] = $bService['forcedReason'];
if( ArrayUtils::has($bService, 'operations') ) {
if ( !ArrayUtils::has($mergedService, 'operations') ) {
$mergedService['operations'] = array();
}
}
$mergedService['operations'] = ArrayUtils::get($bService, 'operations');
}
$mergedServicesById[$serviceId] = $mergedService;
}
return $mergedServicesById;
}
/**
* @property {{
* shortenDeadlineHrs: number,
* shortenDeadlinePricePerPage: number,
* boostWriterCategoryId: number,
* boostWriterPercent: number,
* newPagesQuantity: number,
* newSlidesQuantity: number,
* newChartsQuantity: number,
* getUsedSourcesOn: boolean,
* getSamplesOn: boolean,
* getProgressiveDeliveryOn: boolean,
* expertProofreading: boolean,
* }} AddServicesInfo
*/
/**
* Calculates additional services by extracting original services
* @param {AddServicesInfo} addServicesInfo
* @param {OrderInfo} originalOrder
* @returns {CostInfo}
*/
public function calculateAddServices( $addServicesInfo, $originalOrder ) {
$originalOrder = $this->orderWithDefaults( $originalOrder );
$addServicesInfo = $this->addServicesInfoWithDefaults($addServicesInfo, $originalOrder);
$mutableOrder = $originalOrder;
$addServicesById = array();
for ($i = 0; $i < count(self::ADD_SERVICES_ORDER); $i += 1) {
$type_id = self::ADD_SERVICES_ORDER[$i];
$addService = $this->calculateAddService($addServicesInfo, $mutableOrder, $type_id);
$addServicesById[$type_id] = $addService;
}
return $this->formatServicesById( $addServicesById );
}
public function extrudeServices($addServicesById, $order) {
$order = $this->orderWithDefaults($order);
// Now the craziness begins
// We merge all the underlying services except those, which belong to UNMERGEABLE_ADD_SERVICES
$mergedServicesById = $this->calculateServices($this->orderWithDefaults());
$extrudedServicesById = [];
for ($i = 0; $i < count(self::ADD_SERVICES_ORDER); $i += 1) {
$type_id = self::ADD_SERVICES_ORDER[$i];
if (self::UNMERGEABLE_ADD_SERVICES.strpos($type_id) == -1) {
$addService = $addServicesById[$type_id];
$mergedServicesById = $this->mergeServices($mergedServicesById, $addService['underlyingServicesById']);
} else {
$extrudedServicesById[$type_id] = $addServicesById[$type_id];
}
}
$extrudedServicesById = array_merge($extrudedServicesById, $mergedServicesById);
$rawCost = $this->getAdditionalServicesRawCost($addServicesById);
$couponsReduction = 0;
foreach ($mergedServicesById as $k) {
$couponsReduction += $mergedServicesById[$k]['winbackReduction'] || 0;
}
$discountServices = $this->calculateDiscountServices($order, $rawCost, $couponsReduction);
$extrudedServicesById = array_merge($extrudedServicesById, $discountServices);
return $this->formatServicesById($extrudedServicesById);
}
public function getLastCalcServForAddServices( $addServicesById ) {
$lastAddServiceId = self::ADD_SERVICES_ORDER[ count(self::ADD_SERVICES_ORDER) - 1];
return $addServicesById[ $lastAddServiceId ]['underlyingServicesById'];
}
}