.
*
* You can get complete code of ProjeQtOr, other resource, help and information
* about contributors at http://www.projeqtor.org
*
* ** DO NOT REMOVE THIS NOTICE *********************************************** */
/** ============================================================================
* creation of the description of the content for a bill.
*/
require_once(__DIR__ . '/persistence/_securityCheck.php');
class BillMain extends SqlElement
{
const DEFAULT_RECIPIENT = 1;
// List of fields that will be exposed in general user interface
public $_sec_description;
public $id; // redefine $id to specify its visible place
public $reference;
public $name;
public $idBillType;
public $idProject;
public $idUser;
public $creationDate;
public $date;
public $idPaymentDelay;
public $paymentDueDate;
public $idClient;
public $idContact;
public $idRecipient;
public $Origin;
public $_spe_billingType;
public $_sec_treatment;
public $billId;
public $idStatus;
public $idResource;
public $sendDate;
public $idDeliveryMode;
public $done;
public $idle;
public $cancelled;
public $_lib_cancelled;
public $_tab_5_1_smallLabel = array('untaxedAmountShort', 'tax', '', 'fullAmountShort', 'commandAmountPctShort', 'amount');
public $untaxedAmount;
public $taxPct;
public $taxAmount;
public $fullAmount;
public $commandAmountPct;
public $_tab_3_1_smallLabel = array('date', 'amount', 'paymentComplete', 'payment');
public $paymentDate;
public $paymentAmount;
public $paymentDone;
public $_spe_paymentsList;
public $paymentsCount;
public $description;
public $billingType;
//public $_sec_BillLine;
public $_BillLine = array();
public $_BillLine_colSpan = "2";
public $_sec_Link;
public $_Link = array();
public $_Attachment = array();
public $_Note = array();
public $_nbColMax = 3;
// Define the layout that will be used for lists
private static $_layout = '
# ${id}
${reference}
${idClient}
${idProject}
${name}
${date}
${idRecipient}
${fullAmount}
${done}
${idle}
';
private static $_fieldsAttributes = array('name' => 'required', 'id' => 'nobr',
'idStatus' => 'required',
'idBillType' => 'required',
'idProject' => 'required',
'billId' => 'hidden',
'taxAmount' => 'calculated,readonly',
'idPrec' => 'required',
'billingType' => 'hidden',
'fullAmount' => 'readonly',
'untaxedAmount' => 'readonly',
"idle" => "nobr",
"cancelled" => "nobr",
'paymentDueDate' => 'readonly',
'paymentsCount' => 'hidden'
);
private static $_colCaptionTransposition = array('description' => 'comment',
'idContact' => 'billContact',
'idPaymentDelay' => 'paymentDelay',
'idDeliveryMode' => 'sendMode',
"idUser" => "issuer",
'idResource' => 'responsible',
'paymentDone' => 'paymentComplete'
);
private static $_databaseColumnName = array('taxPct' => 'tax');
public $_calculateForColumn = array("name" => "concat(coalesce(reference,''),' - ',name,' (',coalesce(fullAmount,0),')')");
/** ==========================================================================
* Constructor
* @param $id the id of the object in the database (null if not stored yet)
* @return void
*/
function __construct($id = NULL, $withoutDependentObjects = false)
{
parent::__construct($id, $withoutDependentObjects);
if (!$this->id) {
$this->commandAmountPct = 100;
}
if ($this->done) {
self::$_fieldsAttributes['idClient'] = 'readonly';
self::$_fieldsAttributes['idBillType'] = 'readonly';
self::$_fieldsAttributes['date'] = 'readonly';
self::$_fieldsAttributes['idProject'] = 'readonly';
self::$_fieldsAttributes['idRecipient'] = 'readonly';
self::$_fieldsAttributes['idContact'] = 'readonly';
self::$_fieldsAttributes['taxPct'] = 'readonly';
self::$_fieldsAttributes['idPaymentDelay'] = 'readonly';
}
if (count($this->_BillLine)) {
self::$_fieldsAttributes['idProject'] = 'readonly';
}
if ($this->fullAmount) {
$this->taxAmount = $this->fullAmount - $this->untaxedAmount;
}
if ($this->paymentDone) {
self::$_fieldsAttributes['paymentDate'] = 'readonly';
self::$_fieldsAttributes['paymentAmount'] = 'readonly';
}
if ($this->paymentsCount > 0) {
self::$_fieldsAttributes['paymentDate'] = 'readonly';
self::$_fieldsAttributes['paymentAmount'] = 'readonly';
self::$_fieldsAttributes['paymentDone'] = 'readonly';
}
}
/** ==========================================================================
* Destructor
* @return void
*/
function __destruct()
{
parent::__destruct();
}
// ============================================================================**********
// GET STATIC DATA FUNCTIONS
// ============================================================================**********
/** ==========================================================================
* Return the specific layout
* @return the layout
*/
protected function getStaticLayout()
{
return self::$_layout;
}
/** ==========================================================================
* Return the specific fieldsAttributes
* @return the fieldsAttributes
*/
protected function getStaticFieldsAttributes()
{
return self::$_fieldsAttributes;
}
/** ============================================================================
* Return the specific colCaptionTransposition
* @return the colCaptionTransposition
*/
protected function getStaticColCaptionTransposition($fld = null)
{
return self::$_colCaptionTransposition;
}
/** ========================================================================
* Return the specific databaseTableName
* @return the databaseTableName
*/
protected function getStaticDatabaseColumnName()
{
return self::$_databaseColumnName;
}
/** =========================================================================
* control data corresponding to Model constraints
* @param void
* @return "OK" if controls are good or an error message
* must be redefined in the inherited class
*/
public function control()
{
$result = "";
// When bill is done
if ($this->done) {
// some data is mandatory
if (!$this->date) {
$result.=" " . LocaleUtil::istanza()->i18n('messageMandatory', array(LocaleUtil::istanza()->i18n('colDate')));
}
if (!trim($this->idClient)) {
$result.=" " . LocaleUtil::istanza()->i18n('messageMandatory', array(LocaleUtil::istanza()->i18n('colIdClient')));
}
if (!trim($this->idContact)) {
$result.=" " . LocaleUtil::istanza()->i18n('messageMandatory', array(LocaleUtil::istanza()->i18n('colIdContact')));
}
if (!trim($this->idRecipient)) {
$result.=" " . LocaleUtil::istanza()->i18n('messageMandatory', array(LocaleUtil::istanza()->i18n('colIdRecipient')));
}
// Lines must exist when bill is done
if (!$this->id) {
$result.=" " . LocaleUtil::istanza()->i18n('errorEmptyBill');
} else {
$line = new BillLine();
$crit = array("refId" => $this->id);
$lineList = $line->getSqlElementsFromCriteria($crit, false);
if (count($lineList) == 0) {
$result.=" " . LocaleUtil::istanza()->i18n('errorEmptyBill');
}
}
}
$defaultControl = parent::control();
if ($defaultControl != 'OK') {
$result.=$defaultControl;
}
if ($result == "") {
$result = 'OK';
}
return $result;
}
/** =========================================================================
* Overrides SqlElement::deleteControl() function to add specific treatments
* @see persistence/SqlElement#deleteControl()
* @return the return message of persistence/SqlElement#deleteControl() method
*/
public function deleteControl()
{
$result = "";
// Cannot delete done bill
$status = new Status($this->idStatus);
if ($status->setDoneStatus) {
$result .= " " . LocaleUtil::istanza()->i18n("errorDeleteDoneBill");
}
// Cannot delete bill with lines
/* $line = new BillLine();
$crit = array("refId"=>$this->id);
$lineList = $line->getSqlElementsFromCriteria($crit,false);
if (count($lineList)>0) {
$result.=" " . LocaleUtil::istanza()->i18n('errorControlDelete') . " - " .LocaleUtil::istanza()->i18n('BillLine') . " (" . count($lineList) . ")";; ;
} */
if (!$result) {
$result = parent::deleteControl();
}
return $result;
}
/** =========================================================================
* Overrides SqlElement::delete() function to add specific treatments
* @see persistence/SqlElement#delete()
* @return the return message of persistence/SqlElement#delete() method
*/
public function delete()
{
$result = parent::delete();
if (!strpos($result, 'id="lastOperationStatus" value="OK"')) {
return $result;
}
return $result;
}
private function createTermBillLine(&$line, $bill_id, $term)
{
$billLine = new BillLine();
$billLine->refType = 'Bill';
$billLine->refId = $bill_id;
$billLine->line = $line++;
$billLine->quantity = 1;
// $billLine->description = $term->description; Impostato al save
// $billLine->detail; Impostato al save
// $billLine->price = $term->amount; Impostato al save
$billLine->idMeasureUnit = NULL;
// $billLine->amount = $term->amount; Impostato al save
$billLine->idTerm = $term->id;
$billLine->idResource;
$billLine->idActivityPrice = NULL;
$billLine->startDate = NULL;
$billLine->endDate = NULL;
$billLine->extra = 0;
$billLine->billingType = BillType::BILL_AT_TERMS;
$result = $billLine->save();
if ($billLine->id) {
$result = '';
// $term->idBill = $bill_id; ! Lo ha già fatto il save
// $term->save();
}
return $result;
}
private function createProdWorkBillLine(&$line, $bill_id, $billingType, $idResource, $idActivityPrice)
{
$billLine = new BillLine();
$billLine->refType = 'Bill';
$billLine->refId = $bill_id;
$billLine->line = $line++;
$billLine->quantity = 1;
// $billLine->description = $term->description; Impostato al save
// $billLine->detail; Impostato al save
// $billLine->price = $term->amount; Impostato al save
$billLine->idMeasureUnit = NULL;
// $billLine->amount = $term->amount; Impostato al save
$billLine->idTerm = null;
$billLine->idResource = $idResource;
$billLine->idActivityPrice = $idActivityPrice;
$billLine->startDate = NULL;
$billLine->endDate = NULL;
$billLine->extra = 0;
$billLine->billingType = $billingType;
$result = $billLine->save();
if (!$billLine->id) {
return $result;
}
if ($billLine->amount == 0) {
$billLine->delete();
return false;
}
return '';
}
private function createManualBillLine(&$line, $bill_id, $quantity, $price, $description)
{
$billLine = new BillLine();
$billLine->refType = 'Bill';
$billLine->refId = $bill_id;
$billLine->line = $line++;
$billLine->quantity = $quantity;
$billLine->description = $description;
$billLine->detail;
$billLine->price = $price;
$billLine->idMeasureUnit = 3;
$billLine->amount = $quantity * $price;
$billLine->idTerm = NULL;
$billLine->idResource;
$billLine->idActivityPrice = NULL;
$billLine->startDate = NULL;
$billLine->endDate = NULL;
$billLine->extra = 0;
$billLine->billingType = BillType::BILL_MANUAL;
$result = $billLine->save();
if ($billLine->id) {
$result = '';
}
return $result;
}
private function createExpenseBillLine(&$line, $bill_id, $expense)
{
if ($expense->realAmount == 0) {
return false;
}
$billLine = new BillLine();
$billLine->refType = 'Bill';
$billLine->refId = $bill_id;
$billLine->line = $line++;
$billLine->quantity = 1;
$billLine->description = $expense->name . " Expense";
$billLine->detail;
$billLine->price = $expense->realAmount;
$billLine->idMeasureUnit = 1;
$billLine->amount = $expense->realAmount;
$billLine->idTerm = NULL;
$billLine->idResource;
$billLine->idActivityPrice = NULL;
$billLine->startDate = $expense->realDate;
$billLine->endDate = $expense->realDate;
$billLine->extra = 0;
$billLine->billingType = BillType::BILL_MANUAL;
$result = $billLine->save();
if (!$billLine->id) {
return $result;
}
$expense->idBill = $bill_id;
$expense->save();
return '';
}
private function createBill($project, $idBillType, $billingType)
{
$this->idProject = $project->id;
$this->name = $project->name;
$this->description = $project->description;
$this->creationDate = date('Y-m-d');
$this->idClient = $project->idClient;
$this->idUser = Session::istanza()->getCurrentUserId();
$this->idStatus = Status::STATUS_RECORDED;
$this->idResource = $project->idResource;
$this->reference = ''; // Cos'è ?????
$this->idBillType = $idBillType; // FINAL_BILL COMPLETE_BILL
$this->date = date('Y-m-d'); // ????
$this->idPaymentDelay = NULL; // Verrà impostato nel save o prendere dal project o no
$this->paymentDueDate = NULL;
$this->idRecipient = self::DEFAULT_RECIPIENT;
$this->Origin = ''; // Cos'è ?????
$this->billId = NULL; // Verrà impostato nel save
$this->done = 0;
$this->idle = 0;
$this->cancelled = 0;
$this->untaxedAmount = 0; // Verrà impostato nel save
$this->taxPct = 0; // Verrà impostato nel save
$this->taxAmount = 0; // ??
$this->fullAmount = 0; // Verrà impostato nel save
$this->commandAmountPct;
$this->paymentDate = NULL;
$this->paymentAmount = NULL;
$this->paymentDone = 0;
$this->paymentsCount = 0;
$this->billingType = $billingType; // BILL_AT_TERMS BILL_CAP_PROD_WORK BILL_PROD_WORK
$this->idDeliveryMode = NULL;
$this->sendDate = NULL;
$this->idContact = $project->idContact;
return $this->simpleSave(); // Per avere un id
}
public function billedUntaxedAmount($idProject)
{
$crit = array("idProject" => $idProject, "cancelled" => '0');
$billFinder = new Bill($idProject);
$billList = $billFinder->getSqlElementsFromCriteria($crit, false);
$billedAmount = 0;
foreach ($billList as $bill) {
$billedAmount += $bill->untaxedAmount;
}
return $billedAmount;
}
private function resultMessage($message, $value = 'INVALID')
{
$returnValue = '' . $message . '';
$returnValue .= '';
$returnValue .= '';
$returnValue .= '';
return $returnValue;
}
/** =========================================================================
* Overrides SqlElement::deleteControl() function to add specific treatments
* @see persistence/SqlElement#deleteControl()
* @return the return message of persistence/SqlElement#deleteControl() method
*/
/* * =========================================================================
* Overrides SqlElement::save() function to add specific treatments
* @see persistence/SqlElement#save()
* @return the return message of persistence/SqlElement#save() method
*/
public function billFromProject($idProject)
{
$created = false;
if (trim($idProject) == '') {
return $this->resultMessage("Manca id del Progetto");
}
$project = new Project($idProject);
if ($project->idProjectType == ProjectType::FIXED_PRICE) {
$billingType = BillType::BILL_AT_TERMS; // BILL_AT_TERMS BILL_CAP_PROD_WORK BILL_PROD_WORK
} else if ($project->idProjectType == ProjectType::TIME_AND_MATERIALS) {
$billingType = BillType::BILL_PROD_WORK; // Da rivedere
} else if ($project->idProjectType == ProjectType::CTAM) {
$billingType = BillType::BILL_CAP_PROD_WORK; // Da rivedere
} else {
return $this->resultMessage("Non è possibile creare una fattura per questo tipo di progetto");
}
$billedAmount = $this->billedUntaxedAmount($idProject);
$billableAmountLeft = $project->ProjectPlanningElement->totalValidatedCost - $billedAmount;
if ($billableAmountLeft <= 0) {
return $this->resultMessage("Non è possibile creare una fattura per questo progetto : già fatturato l'importo validato");
}
$bill_id = null;
if ($project->idProjectType == ProjectType::FIXED_PRICE) {
$termFinder = new Term();
$termList = $termFinder->getProjectTerms($idProject);
if (count($termList) > 0) {
$term = $termList[0];
if ($term->validatedAmount > $billableAmountLeft) {
return $this->resultMessage("Non è possibile creare questa fattura : l'importo fissato per la scadenza supera l'importo residuo");
} else if ($term->validatedAmount == $billableAmountLeft) {
$idBillType = $billedAmount == 0 ? BillType::COMPLETE_BILL : BillType::FINAL_BILL; //
} else {
$idBillType = BillType::PARTIAL_BILL; //
}
$result = $this->createBill($project, $idBillType, $billingType);
if (!$this->id) {
return $result;
}
$bill_id = $this->id;
$line = 1;
$result = $this->createTermBillLine($line, $bill_id, $term);
if ($result != '') {
return $result;
}
$created = true;
} else if ($project->done) {
$idBillType = $billedAmount == 0 ? BillType::COMPLETE_BILL : BillType::FINAL_BILL; //
$result = $this->createBill($project, $idBillType, $billingType);
if (!$this->id) {
return $result;
}
$bill_id = $this->id;
$line = 1;
$quantity = 1;
$price = $billableAmountLeft;
$result = $this->createManualBillLine($line, $bill_id, $quantity, $price, $description);
if ($result != '') {
return $result;
}
$created = true;
} else {
$result = $this->resultMessage("Non esiste una scadenza per questo progetto. Crearla o chiudere il progetto");
}
} else if ($project->idProjectType == ProjectType::TIME_AND_MATERIALS || $project->idProjectType == ProjectType::CTAM) {
$affectationFinder = new Affectation();
$crit = array("idProject" => $idProject, "idle" => '0');
$affectationList = $affectationFinder->getSqlElementsFromCriteria($crit, false);
$activitypriceFinder = new ActivityPrice();
$activitypriceList = $activitypriceFinder->getSqlElementsFromCriteria($crit, false);
if (count($affectationList) == 0) {
$result = $this->resultMessage("Impossibile creare la fattura per il lavoro :Nessuna risorsa coinvolta in questo progetto");
} else if (count($activitypriceList) == 0) {
$result = $this->resultMessage("Impossibile creare la fattura per il lavoro :Nessun prezzo attività creato per questo progetto");
} else {
$idBillType = BillType::PARTIAL_BILL; //
$result = $this->createBill($project, $idBillType, $billingType);
if (!$this->id) {
return $result;
}
$bill_id = $this->id;
$line = 1;
foreach ($affectationList as $affectation) {
foreach ($activitypriceList as $activityprice) {
$idResource = $affectation->idResource;
$idActivityPrice = $activityprice->id;
$result = $this->createProdWorkBillLine($line, $bill_id, $billingType, $idResource, $idActivityPrice);
if ($result !== false) {
if ($result != '') {
return $result;
}
$created = true;
}
}
}
}
}
// $quotation = SqlElement::getSingleSqlElementFromCriteria('Quotation', $crit, false);
if ($project->ProjectPlanningElement->expenseAssignedAmount >= $project->ProjectPlanningElement->expenseValidatedAmount) {
$expenseFinder = new Expense();
$crit = array("idProject" => $idProject, "cancelled" => '0', "idBill" => null);
$expenseList = $expenseFinder->getSqlElementsFromCriteria($crit, false);
if (count($expenseList) > 0) {
if (!$bill_id) {
$idBillType = BillType::PARTIAL_BILL; //
$result = $this->createBill($project, $idBillType, $billingType);
if (!$this->id) {
return $result;
}
$bill_id = $this->id;
}
foreach ($expenseList as $expense) {
$result = $this->createExpenseBillLine($line, $bill_id, $expense);
if ($result !== false) {
if ($result != '') {
return $result;
}
$created = true;
}
}
}
}
if ($created) {
$result = $this->resultMessage("Bill $bill_id inserted", 'OK');
} else if ($result == '') {
$result = $this->resultMessage("Bill $bill_id not inserted. No amount to bill");
}
return $result;
}
/** =========================================================================
* Overrides SqlElement::save() function to add specific treatments
* @see persistence/SqlElement#save()
* @return the return message of persistence/SqlElement#save() method
*/
public function save()
{
$oldBill = $this->getOld();
// billingType
$proj = new Project($this->idProject);
$type = new ProjectType($proj->idProjectType);
$this->billingType = $type->internalData;
// Calclate bill id
if ($this->done and ! $this->billId) {
$numStart = GlbParameter::istanza()->getGlobalParameter('billNumStart');
$bill = new Bill();
$crit = array("done" => "1");
$billList = $bill->getSqlElementsFromCriteria($crit, false);
$num = count($billList) + $numStart;
$this->billId = $num;
$this->setReference();
}
// Get Client
if (!trim($this->idClient)) {
$this->idClient = $proj->idClient;
}
// get Contact
if (!trim($this->idContact)) {
$this->idContact = $proj->idContact;
}
// Get the tax from Client / Contact / Recipient
if (trim($this->idClient)) {
$client = new Client($this->idClient);
if ($client->taxPct != '' and ! $this->taxPct) {
$this->taxPct = $client->taxPct;
}
if (!trim($this->idPaymentDelay)) {
$this->idPaymentDelay = $client->idPaymentDelay;
}
}
if (trim($this->idRecipient)) {
$recipient = new Recipient($this->idRecipient);
if ($recipient->taxFree) {
$this->taxPct = 0;
}
}
if (trim($this->idPaymentDelay) and $this->date) {
$delay = new PaymentDelay($this->idPaymentDelay);
$date = GlbCalendar::istanza()->addDaysToDate($this->date, $delay->days);
if ($delay->endOfMonth) {
$date = date("Y-m-t", strtotime($date));
}
$this->paymentDueDate = $date;
}
if ($this->paymentAmount == $this->fullAmount and $this->fullAmount > 0) {
$this->paymentDone = 1;
}
// calculate amounts for bill lines
$billLine = new BillLine();
$crit = array("refType" => "Bill", "refId" => $this->id);
$billLineList = $billLine->getSqlElementsFromCriteria($crit, false);
$amount = 0;
foreach ($billLineList as $line) {
$amount+=$line->amount;
}
$this->untaxedAmount = $amount;
$this->fullAmount = $amount * (1 + $this->taxPct / 100);
$this->retreivePayments(false);
$result = parent::save();
return $result;
}
/** ==========================================================================
* Return the validation sript for some fields
* @return the validation javascript (for dojo frameword)
*/
public function getValidationScript($colName)
{
$colScript = parent::getValidationScript($colName);
if ($colName == "untaxedAmount" || $colName == "taxPct") {
$colScript .= '';
} else if ($colName == "idProject") {
$colScript .= '';
} else if ($colName == "idClient") {
$colScript .= '';
}
return $colScript;
}
public function drawSpecificItem($item)
{
// $displayWidth = PrintUtil::istanza()->getDisplayWidth();
// $labelWidth = 175; // To be changed if changes in css file (label and .label)
// $largeWidth = ( ($displayWidth + 30) / 2) - $labelWidth;
$result = "";
if ($item == 'billingType') {
$result .="