Completed
Branch develop (5ab7fa)
by
unknown
30:39
created

CommonInvoice::getVentilExportCompta()   B

Complexity

Conditions 5
Paths 10

Size

Total Lines 29
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 16
nc 10
nop 0
dl 0
loc 29
rs 8.439
c 0
b 0
f 0
1
<?php
2
/* Copyright (C) 2012       Regis Houssin       <[email protected]>
3
 * Copyright (C) 2012       Cédric Salvador     <[email protected]>
4
 * Copyright (C) 2012-2014  Raphaël Doursenaud  <[email protected]>
5
 *
6
 * This program is free software; you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation; either version 3 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18
 */
19
20
/**
21
 *       \file       htdocs/core/class/commoninvoice.class.php
22
 *       \ingroup    core
23
 *       \brief      File of the superclass of invoices classes (customer and supplier)
24
 */
25
26
require_once DOL_DOCUMENT_ROOT .'/core/class/commonobject.class.php';
27
28
/**
29
 * 	Superclass for invoices classes
30
 */
31
abstract class CommonInvoice extends CommonObject
32
{
33
    /**
34
     * Standard invoice
35
     */
36
    const TYPE_STANDARD = 0;
37
38
    /**
39
     * Replacement invoice
40
     */
41
    const TYPE_REPLACEMENT = 1;
42
43
    /**
44
     * Credit note invoice
45
     */
46
    const TYPE_CREDIT_NOTE = 2;
47
48
    /**
49
     * Deposit invoice
50
     */
51
    const TYPE_DEPOSIT = 3;
52
53
    /**
54
     * Proforma invoice.
55
     * @deprectad Remove this. A "proforma invoice" is an order with a look of invoice, not an invoice !
56
     */
57
    const TYPE_PROFORMA = 4;
58
59
    /**
60
     * Situation invoice
61
     */
62
    const TYPE_SITUATION = 5;
63
64
	/**
65
	 * Draft
66
	 */
67
	const STATUS_DRAFT = 0;
68
69
	/**
70
	 * Validated (need to be paid)
71
	 */
72
	const STATUS_VALIDATED = 1;
73
74
	/**
75
	 * Classified paid.
76
	 * If paid partially, $this->close_code can be:
77
	 * - CLOSECODE_DISCOUNTVAT
78
	 * - CLOSECODE_BADDEBT
79
	 * If paid completelly, this->close_code will be null
80
	 */
81
	const STATUS_CLOSED = 2;
82
83
	/**
84
	 * Classified abandoned and no payment done.
85
	 * $this->close_code can be:
86
	 * - CLOSECODE_BADDEBT
87
	 * - CLOSECODE_ABANDONED
88
	 * - CLOSECODE_REPLACED
89
	 */
90
	const STATUS_ABANDONED = 3;
91
92
93
	/**
94
	 * 	Return remain amount to pay. Property ->id and ->total_ttc must be set.
95
	 *  This does not include open direct debit requests.
96
	 *
97
	 *  @param 		int 	$multicurrency 	Return multicurrency_amount instead of amount
98
	 *	@return		double						Remain of amount to pay
99
	 */
100
	function getRemainToPay($multicurrency=0)
101
	{
102
	    $alreadypaid=0;
103
	    $alreadypaid+=$this->getSommePaiement($multicurrency);
104
	    $alreadypaid+=$this->getSumDepositsUsed($multicurrency);
105
	    $alreadypaid+=$this->getSumCreditNotesUsed($multicurrency);
106
    	return $this->total_ttc - $alreadypaid;
107
	}
108
109
	/**
110
	 * 	Return amount of payments already done
111
	 *
112
	 *  @param 		int 	$multicurrency 	Return multicurrency_amount instead of amount
113
	 *	@return		int						Amount of payment already done, <0 if KO
114
	 */
115
	function getSommePaiement($multicurrency=0)
116
	{
117
		$table='paiement_facture';
118
		$field='fk_facture';
119
		if ($this->element == 'facture_fourn' || $this->element == 'invoice_supplier')
120
		{
121
			$table='paiementfourn_facturefourn';
122
			$field='fk_facturefourn';
123
		}
124
125
		$sql = 'SELECT sum(amount) as amount, sum(multicurrency_amount) as multicurrency_amount';
126
		$sql.= ' FROM '.MAIN_DB_PREFIX.$table;
127
		$sql.= ' WHERE '.$field.' = '.$this->id;
128
129
		dol_syslog(get_class($this)."::getSommePaiement", LOG_DEBUG);
130
		$resql=$this->db->query($sql);
131
		if ($resql)
132
		{
133
			$obj = $this->db->fetch_object($resql);
134
			$this->db->free($resql);
135
			if ($multicurrency) return $obj->multicurrency_amount;
136
			else return $obj->amount;
137
		}
138
		else
139
		{
140
			$this->error=$this->db->lasterror();
141
			return -1;
142
		}
143
	}
144
145
	/**
146
	 *    	Return amount (with tax) of all deposits invoices used by invoice.
147
     *      Should always be empty, except if option FACTURE_DEPOSITS_ARE_JUST_PAYMENTS is on (not recommended).
148
	 *
149
	 * 		@param 		int 	$multicurrency 	Return multicurrency_amount instead of amount
150
	 *		@return		int						<0 if KO, Sum of deposits amount otherwise
151
	 */
152
	function getSumDepositsUsed($multicurrency=0)
153
	{
154
		if ($this->element == 'facture_fourn' || $this->element == 'invoice_supplier')
155
	    {
156
	        // TODO
157
	       return 0;
158
	    }
159
160
	    require_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
161
162
	    $discountstatic=new DiscountAbsolute($this->db);
163
	    $result=$discountstatic->getSumDepositsUsed($this, $multicurrency);
164
	    if ($result >= 0)
165
	    {
166
	        return $result;
167
	    }
168
	    else
169
	    {
170
	        $this->error=$discountstatic->error;
171
	        return -1;
172
	    }
173
	}
174
175
	/**
176
	 *    	Return amount (with tax) of all credit notes and deposits invoices used by invoice
177
	 *
178
	 * 		@param 		int 	$multicurrency 	Return multicurrency_amount instead of amount
179
	 *		@return		int						<0 if KO, Sum of credit notes and deposits amount otherwise
180
	 */
181
	function getSumCreditNotesUsed($multicurrency=0)
182
	{
183
	    if ($this->element == 'facture_fourn' || $this->element == 'invoice_supplier')
184
	    {
185
	        // TODO
186
	        return 0;
187
	    }
188
189
	    require_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
190
191
	    $discountstatic=new DiscountAbsolute($this->db);
192
	    $result=$discountstatic->getSumCreditNotesUsed($this, $multicurrency);
193
	    if ($result >= 0)
194
	    {
195
	        return $result;
196
	    }
197
	    else
198
	    {
199
	        $this->error=$discountstatic->error;
200
	        return -1;
201
	    }
202
	}
203
204
	/**
205
	 *	Renvoie tableau des ids de facture avoir issus de la facture
206
	 *
207
	 *	@return		array		Tableau d'id de factures avoirs
208
	 */
209
	function getListIdAvoirFromInvoice()
210
	{
211
		$idarray=array();
212
213
		$sql = 'SELECT rowid';
214
		$sql.= ' FROM '.MAIN_DB_PREFIX.$this->table_element;
215
		$sql.= ' WHERE fk_facture_source = '.$this->id;
216
		$sql.= ' AND type = 2';
217
		$resql=$this->db->query($sql);
218
		if ($resql)
219
		{
220
			$num = $this->db->num_rows($resql);
221
			$i = 0;
222
			while ($i < $num)
223
			{
224
				$row = $this->db->fetch_row($resql);
225
				$idarray[]=$row[0];
226
				$i++;
227
			}
228
		}
229
		else
230
		{
231
			dol_print_error($this->db);
232
		}
233
		return $idarray;
234
	}
235
236
	/**
237
	 *	Renvoie l'id de la facture qui la remplace
238
	 *
239
	 *	@param		string	$option		filtre sur statut ('', 'validated', ...)
240
	 *	@return		int					<0 si KO, 0 si aucune facture ne remplace, id facture sinon
241
	 */
242
	function getIdReplacingInvoice($option='')
243
	{
244
		$sql = 'SELECT rowid';
245
		$sql.= ' FROM '.MAIN_DB_PREFIX.$this->table_element;
246
		$sql.= ' WHERE fk_facture_source = '.$this->id;
247
		$sql.= ' AND type < 2';
248
		if ($option == 'validated') $sql.= ' AND fk_statut = 1';
249
		// PROTECTION BAD DATA
250
		// Au cas ou base corrompue et qu'il y a une facture de remplacement validee
251
		// et une autre non, on donne priorite a la validee.
252
		// Ne devrait pas arriver (sauf si acces concurrentiel et que 2 personnes
253
		// ont cree en meme temps une facture de remplacement pour la meme facture)
254
		$sql.= ' ORDER BY fk_statut DESC';
255
256
		$resql=$this->db->query($sql);
257
		if ($resql)
258
		{
259
			$obj = $this->db->fetch_object($resql);
260
			if ($obj)
261
			{
262
				// Si il y en a
263
				return $obj->rowid;
264
			}
265
			else
266
			{
267
				// Si aucune facture ne remplace
268
				return 0;
269
			}
270
		}
271
		else
272
		{
273
			return -1;
274
		}
275
	}
276
277
	/**
278
	 *  Return if an invoice can be deleted
279
	 *	Rule is:
280
	 *  If invoice is draft and has a temporary ref -> yes
281
	 *  If hidden option INVOICE_CAN_NEVER_BE_REMOVED is on -> no (0)
282
	 *  If invoice is dispatched in bookkeeping -> no (-1)
283
	 *  If invoice has a definitive ref, is not last and INVOICE_CAN_ALWAYS_BE_REMOVED off -> no (-2)
284
	 *  If invoice not last in a cycle -> no (-3)
285
	 *  If there is payment -> no (-4)
286
	 *
287
	 *  @return    int         <=0 if no, >0 if yes
288
	 */
289
	function is_erasable()
290
	{
291
		global $conf;
292
293
		// We check if invoice is a temporary number (PROVxxxx)
294
		$tmppart = substr($this->ref, 1, 4);
295
296
		if ($this->statut == self::STATUS_DRAFT && $tmppart === 'PROV') // If draft invoice and ref not yet defined
297
		{
298
			return 1;
299
		}
300
301
		if (! empty($conf->global->INVOICE_CAN_NEVER_BE_REMOVED)) return 0;
302
303
		// If not a draft invoice and not temporary invoice
304
		if ($tmppart !== 'PROV')
305
		{
306
			$ventilExportCompta = $this->getVentilExportCompta();
307
			if ($ventilExportCompta != 0) return -1;
308
309
			// Get last number of validated invoice
310
			if ($this->element != 'invoice_supplier')
311
			{
312
				if (empty($this->thirdparty)) $this->fetch_thirdparty();	// We need to have this->thirdparty defined, in case of numbering rule use tags that depend on thirdparty (like {t} tag).
313
				$maxfacnumber = $this->getNextNumRef($this->thirdparty,'last');
1 ignored issue
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class CommonInvoice as the method getNextNumRef() does only exist in the following sub-classes of CommonInvoice: Facture, FactureFournisseur. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
314
315
				// If there is no invoice into the reset range and not already dispatched, we can delete
316
				// If invoice to delete is last one and not already dispatched, we can delete
317
				if (empty($conf->global->INVOICE_CAN_ALWAYS_BE_REMOVED) && $maxfacnumber != '' && $maxfacnumber != $this->ref) return -2;
318
319
				// TODO If there is payment in bookkeeping, check payment is not dispatched in accounting
320
				// ...
321
322
				if ($this->situation_cycle_ref) {
323
					$last = $this->is_last_in_cycle();
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class CommonInvoice as the method is_last_in_cycle() does only exist in the following sub-classes of CommonInvoice: Facture. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
324
					if (! $last) return -3;
325
				}
326
			}
327
		}
328
329
		// Test if there is at least one payment. If yes, refuse to delete.
330
		if (empty($conf->global->INVOICE_CAN_ALWAYS_BE_REMOVED) && $this->getSommePaiement() > 0) return -4;
331
332
		return 1;
333
	}
334
335
	/**
336
	 *	Return if an invoice was dispatched in bookkeeping
337
	 *
338
	 *	@return     int         <0 if KO, 0=no, 1=yes
339
	 */
340
	function getVentilExportCompta()
341
	{
342
		$alreadydispatched = 0;
343
344
		$type = 'customer_invoice';
345
		if ($this->element == 'invoice_supplier') $type = 'supplier_invoice';
346
347
		$sql = " SELECT COUNT(ab.rowid) as nb FROM ".MAIN_DB_PREFIX."accounting_bookkeeping as ab WHERE ab.doc_type='".$type."' AND ab.fk_doc = ".$this->id;
348
		$resql = $this->db->query($sql);
349
		if ($resql)
350
		{
351
			$obj = $this->db->fetch_object($resql);
352
			if ($obj)
353
			{
354
				$alreadydispatched = $obj->nb;
355
			}
356
		}
357
		else
358
		{
359
			$this->error = $this->db->lasterror();
360
			return -1;
361
		}
362
363
		if ($alreadydispatched)
364
		{
365
			return 1;
366
		}
367
		return 0;
368
	}
369
370
371
	/**
372
	 *	Return label of type of invoice
373
	 *
374
	 *	@return     string        Label of type of invoice
375
	 */
376
	function getLibType()
377
	{
378
		global $langs;
379
        if ($this->type == CommonInvoice::TYPE_STANDARD) return $langs->trans("InvoiceStandard");
380
        if ($this->type == CommonInvoice::TYPE_REPLACEMENT) return $langs->trans("InvoiceReplacement");
381
        if ($this->type == CommonInvoice::TYPE_CREDIT_NOTE) return $langs->trans("InvoiceAvoir");
382
        if ($this->type == CommonInvoice::TYPE_DEPOSIT) return $langs->trans("InvoiceDeposit");
383
        if ($this->type == CommonInvoice::TYPE_PROFORMA) return $langs->trans("InvoiceProForma");           // Not used.
384
        if ($this->type == CommonInvoice::TYPE_SITUATION) return $langs->trans("InvoiceSituation");
385
		return $langs->trans("Unknown");
386
	}
387
388
	/**
389
	 *  Return label of object status
390
	 *
391
	 *  @param      int		$mode			0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=short label + picto, 6=Long label + picto
392
	 *  @param      integer	$alreadypaid    0=No payment already done, >0=Some payments were already done (we recommand to put here amount payed if you have it, 1 otherwise)
393
	 *  @return     string			        Label of status
394
	 */
395
	function getLibStatut($mode=0, $alreadypaid=-1)
396
	{
397
		return $this->LibStatut($this->paye, $this->statut, $mode, $alreadypaid, $this->type);
398
	}
399
400
	/**
401
	 *	Return label of a status
402
	 *
403
	 *	@param    	int  	$paye          	Status field paye
404
	 *	@param      int		$status        	Id status
405
	 *	@param      int		$mode          	0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=short label + picto, 6=long label + picto
406
	 *	@param		integer	$alreadypaid	0=No payment already done, >0=Some payments were already done (we recommand to put here amount payed if you have it, -1 otherwise)
407
	 *	@param		int		$type			Type invoice
408
	 *	@return     string        			Label of status
409
	 */
410
	function LibStatut($paye, $status, $mode=0, $alreadypaid=-1, $type=0)
411
	{
412
		global $langs;
413
		$langs->load('bills');
414
415
		//print "$paye,$status,$mode,$alreadypaid,$type";
416
		if ($mode == 0)
417
		{
418
			$prefix='';
419
			if (! $paye)
420
			{
421
				if ($status == 0) return $langs->trans('Bill'.$prefix.'StatusDraft');
422
				if (($status == 3 || $status == 2) && $alreadypaid <= 0) return $langs->trans('Bill'.$prefix.'StatusClosedUnpaid');
423
				if (($status == 3 || $status == 2) && $alreadypaid > 0) return $langs->trans('Bill'.$prefix.'StatusClosedPaidPartially');
424
				if ($alreadypaid <= 0) return $langs->trans('Bill'.$prefix.'StatusNotPaid');
425
				return $langs->trans('Bill'.$prefix.'StatusStarted');
426
			}
427
			else
428
			{
429
				if ($type == self::TYPE_CREDIT_NOTE) return $langs->trans('Bill'.$prefix.'StatusPaidBackOrConverted');       // credit note
430
				elseif ($type == self::TYPE_DEPOSIT) return $langs->trans('Bill'.$prefix.'StatusConverted');             // deposit invoice
431
				else return $langs->trans('Bill'.$prefix.'StatusPaid');
432
			}
433
		}
434
		if ($mode == 1)
435
		{
436
			$prefix='Short';
437
			if (! $paye)
438
			{
439
				if ($status == 0) return $langs->trans('Bill'.$prefix.'StatusDraft');
440
				if (($status == 3 || $status == 2) && $alreadypaid <= 0) return $langs->trans('Bill'.$prefix.'StatusCanceled');
441
				if (($status == 3 || $status == 2) && $alreadypaid > 0) return $langs->trans('Bill'.$prefix.'StatusClosedPaidPartially');
442
				if ($alreadypaid <= 0) return $langs->trans('Bill'.$prefix.'StatusNotPaid');
443
				return $langs->trans('Bill'.$prefix.'StatusStarted');
444
			}
445
			else
446
			{
447
				if ($type == self::TYPE_CREDIT_NOTE) return $langs->trans('Bill'.$prefix.'StatusPaidBackOrConverted');
448
				elseif ($type == self::TYPE_DEPOSIT) return $langs->trans('Bill'.$prefix.'StatusConverted');
449
				else return $langs->trans('Bill'.$prefix.'StatusPaid');
450
			}
451
		}
452
		if ($mode == 2)
453
		{
454
			$prefix='Short';
455
			if (! $paye)
456
			{
457
				if ($status == 0) return img_picto($langs->trans('BillStatusDraft'),'statut0').' '.$langs->trans('Bill'.$prefix.'StatusDraft');
458
				if (($status == 3 || $status == 2) && $alreadypaid <= 0) return img_picto($langs->trans('StatusCanceled'),'statut5').' '.$langs->trans('Bill'.$prefix.'StatusCanceled');
459
				if (($status == 3 || $status == 2) && $alreadypaid > 0) return img_picto($langs->trans('BillStatusClosedPaidPartially'),'statut7').' '.$langs->trans('Bill'.$prefix.'StatusClosedPaidPartially');
460
				if ($alreadypaid <= 0) return img_picto($langs->trans('BillStatusNotPaid'),'statut1').' '.$langs->trans('Bill'.$prefix.'StatusNotPaid');
461
				return img_picto($langs->trans('BillStatusStarted'),'statut3').' '.$langs->trans('Bill'.$prefix.'StatusStarted');
462
			}
463
			else
464
			{
465
				if ($type == self::TYPE_CREDIT_NOTE) return img_picto($langs->trans('BillStatusPaidBackOrConverted'),'statut6').' '.$langs->trans('Bill'.$prefix.'StatusPaidBackOrConverted');
466
				elseif ($type == self::TYPE_DEPOSIT) return img_picto($langs->trans('BillStatusConverted'),'statut6').' '.$langs->trans('Bill'.$prefix.'StatusConverted');
467
				else return img_picto($langs->trans('BillStatusPaid'),'statut6').' '.$langs->trans('Bill'.$prefix.'StatusPaid');
468
			}
469
		}
470
		if ($mode == 3)
471
		{
472
			$prefix='Short';
473
			if (! $paye)
474
			{
475
				if ($status == 0) return img_picto($langs->trans('BillStatusDraft'),'statut0');
476
				if (($status == 3 || $status == 2) && $alreadypaid <= 0) return img_picto($langs->trans('BillStatusCanceled'),'statut5');
477
				if (($status == 3 || $status == 2) && $alreadypaid > 0) return img_picto($langs->trans('BillStatusClosedPaidPartially'),'statut7');
478
				if ($alreadypaid <= 0) return img_picto($langs->trans('BillStatusNotPaid'),'statut1');
479
				return img_picto($langs->trans('BillStatusStarted'),'statut3');
480
			}
481
			else
482
			{
483
				if ($type == self::TYPE_CREDIT_NOTE) return img_picto($langs->trans('BillStatusPaidBackOrConverted'),'statut6');
484
				elseif ($type == self::TYPE_DEPOSIT) return img_picto($langs->trans('BillStatusConverted'),'statut6');
485
				else return img_picto($langs->trans('BillStatusPaid'),'statut6');
486
			}
487
		}
488
		if ($mode == 4)
489
		{
490
			$prefix='';
491
			if (! $paye)
492
			{
493
				if ($status == 0) return img_picto($langs->trans('BillStatusDraft'),'statut0').' '.$langs->trans('BillStatusDraft');
494
				if (($status == 3 || $status == 2) && $alreadypaid <= 0) return img_picto($langs->trans('BillStatusCanceled'),'statut5').' '.$langs->trans('Bill'.$prefix.'StatusCanceled');
495
				if (($status == 3 || $status == 2) && $alreadypaid > 0) return img_picto($langs->trans('BillStatusClosedPaidPartially'),'statut7').' '.$langs->trans('Bill'.$prefix.'StatusClosedPaidPartially');
496
				if ($alreadypaid <= 0) return img_picto($langs->trans('BillStatusNotPaid'),'statut1').' '.$langs->trans('BillStatusNotPaid');
497
				return img_picto($langs->trans('BillStatusStarted'),'statut3').' '.$langs->trans('BillStatusStarted');
498
			}
499
			else
500
			{
501
				if ($type == self::TYPE_CREDIT_NOTE) return img_picto($langs->trans('BillStatusPaidBackOrConverted'),'statut6').' '.$langs->trans('BillStatusPaidBackOrConverted');
502
				elseif ($type == self::TYPE_DEPOSIT) return img_picto($langs->trans('BillStatusConverted'),'statut6').' '.$langs->trans('BillStatusConverted');
503
				else return img_picto($langs->trans('BillStatusPaid'),'statut6').' '.$langs->trans('BillStatusPaid');
504
			}
505
		}
506
		if ($mode == 5 || $mode == 6)
507
		{
508
			$prefix='';
509
			if ($mode == 5) $prefix='Short';
510
			if (! $paye)
511
			{
512
				if ($status == 0) return '<span class="xhideonsmartphone">'.$langs->trans('Bill'.$prefix.'StatusDraft').' </span>'.img_picto($langs->trans('BillStatusDraft'),'statut0');
513
				if (($status == 3 || $status == 2) && $alreadypaid <= 0) return '<span class="xhideonsmartphone">'.$langs->trans('Bill'.$prefix.'StatusCanceled').' </span>'.img_picto($langs->trans('BillStatusCanceled'),'statut5');
514
				if (($status == 3 || $status == 2) && $alreadypaid > 0) return '<span class="xhideonsmartphone">'.$langs->trans('Bill'.$prefix.'StatusClosedPaidPartially').' </span>'.img_picto($langs->trans('BillStatusClosedPaidPartially'),'statut7');
515
				if ($alreadypaid <= 0)
516
				{
517
				    if ($type == self::TYPE_CREDIT_NOTE) return '<span class="xhideonsmartphone">'.$langs->trans('Bill'.$prefix.'StatusNotRefunded').' </span>'.img_picto($langs->trans('StatusNotRefunded'),'statut1');
518
				    return '<span class="xhideonsmartphone">'.$langs->trans('Bill'.$prefix.'StatusNotPaid').' </span>'.img_picto($langs->trans('BillStatusNotPaid'),'statut1');
519
				}
520
				return '<span class="xhideonsmartphone">'.$langs->trans('Bill'.$prefix.'StatusStarted').' </span>'.img_picto($langs->trans('BillStatusStarted'),'statut3');
521
			}
522
			else
523
			{
524
				if ($type == self::TYPE_CREDIT_NOTE) return '<span class="xhideonsmartphone">'.$langs->trans('Bill'.$prefix.'StatusPaidBackOrConverted').' </span>'.img_picto($langs->trans('BillStatusPaidBackOrConverted'),'statut6');
525
				elseif ($type == self::TYPE_DEPOSIT) return '<span class="xhideonsmartphone">'.$langs->trans('Bill'.$prefix.'StatusConverted').' </span>'.img_picto($langs->trans('BillStatusConverted'),'statut6');
526
				else return '<span class="xhideonsmartphone">'.$langs->trans('Bill'.$prefix.'StatusPaid').' </span>'.img_picto($langs->trans('BillStatusPaid'),'statut6');
527
			}
528
		}
529
	}
530
531
	/**
532
	 *	Renvoi une date limite de reglement de facture en fonction des
533
	 *	conditions de reglements de la facture et date de facturation
534
	 *
535
	 *	@param      integer	$cond_reglement   	Condition of payment (code or id) to use. If 0, we use current condition.
536
	 *	@return     date     			       	Date limite de reglement si ok, <0 si ko
537
	 */
538
	function calculate_date_lim_reglement($cond_reglement=0)
539
	{
540
		if (! $cond_reglement) $cond_reglement=$this->cond_reglement_code;
541
		if (! $cond_reglement) $cond_reglement=$this->cond_reglement_id;
542
543
		$cdr_nbjour=0; $cdr_type=0; $cdr_decalage=0;
544
545
		$sqltemp = 'SELECT c.type_cdr,c.nbjour,c.decalage';
546
		$sqltemp.= ' FROM '.MAIN_DB_PREFIX.'c_payment_term as c';
547
		$sqltemp.= " WHERE c.entity IN (" . getEntity('c_payment_term').")";
548
		if (is_numeric($cond_reglement)) $sqltemp.= " AND c.rowid=".$cond_reglement;
549
		else $sqltemp.= " AND c.code='".$this->db->escape($cond_reglement)."'";
550
551
		dol_syslog(get_class($this).'::calculate_date_lim_reglement', LOG_DEBUG);
552
		$resqltemp=$this->db->query($sqltemp);
553
		if ($resqltemp)
554
		{
555
			if ($this->db->num_rows($resqltemp))
556
			{
557
				$obj = $this->db->fetch_object($resqltemp);
558
				$cdr_nbjour = $obj->nbjour;
559
				$cdr_type = $obj->type_cdr;
560
				$cdr_decalage = $obj->decalage;
561
			}
562
		}
563
		else
564
		{
565
			$this->error=$this->db->error();
566
			return -1;
567
		}
568
		$this->db->free($resqltemp);
569
570
		/* Definition de la date limite */
571
572
		// 1 : ajout du nombre de jours
573
		$datelim = $this->date + ($cdr_nbjour * 3600 * 24);
574
575
		// 2 : application de la regle "fin de mois"
576
		if ($cdr_type == 1)
577
		{
578
			$mois=date('m', $datelim);
579
			$annee=date('Y', $datelim);
580
			if ($mois == 12)
581
			{
582
				$mois = 1;
583
				$annee += 1;
584
			}
585
			else
586
			{
587
				$mois += 1;
588
			}
589
			// On se deplace au debut du mois suivant, et on retire un jour
590
			$datelim=dol_mktime(12,0,0,$mois,1,$annee);
591
			$datelim -= (3600 * 24);
592
		}
593
		elseif($cdr_type == 2 && !empty($cdr_nbjour)) // Application de la règle, le N du mois courant ou suivant
594
		{
595
596
			$date_piece = dol_mktime(0,0,0,date('m', $this->date),date('d', $this->date),date('Y', $this->date)); // Sans les heures minutes et secondes
597
			$date_lim_current = dol_mktime(0,0,0,date('m', $this->date),$cdr_nbjour,date('Y', $this->date)); // Sans les heures minutes et secondes
598
			$date_lim_next = strtotime(date('Y-m-d', $date_lim_current).' +1month');
599
600
			$diff = $date_piece - $date_lim_current;
601
602
			if($diff < 0) $datelim = $date_lim_current;
603
			else $datelim = $date_lim_next;
604
605
		}
606
607
		// 3 : application du decalage
608
		$datelim += ($cdr_decalage * 3600 * 24);
609
610
		return $datelim;
611
	}
612
}
613
614
615
616
require_once DOL_DOCUMENT_ROOT .'/core/class/commonobjectline.class.php';
617
618
/**
619
 *	Parent class of all other business classes for details of elements (invoices, contracts, proposals, orders, ...)
620
 */
621
abstract class CommonInvoiceLine extends CommonObjectLine
622
{
623
	/**
624
	 * Quantity
625
	 * @var int
626
	 */
627
	public $qty;
628
629
	/**
630
	 * Unit price before taxes
631
	 * @var float
632
	 */
633
	public $subprice;
634
635
	/**
636
	 * Type of the product. 0 for product 1 for service
637
	 * @var int
638
	 */
639
	public $product_type = 0;
640
641
	/**
642
	 * Id of corresponding product
643
	 * @var int
644
	 */
645
	public $fk_product;
646
647
	/**
648
	 * VAT code
649
	 * @var string
650
	 */
651
	public $vat_src_code;
652
653
	/**
654
	 * VAT %
655
	 * @var float
656
	 */
657
	public $tva_tx;
658
659
	/**
660
	 * Local tax 1 %
661
	 * @var float
662
	 */
663
	public $localtax1_tx;
664
665
	/**
666
	 * Local tax 2 %
667
	 * @var float
668
	 */
669
	public $localtax2_tx;
670
671
	/**
672
	 * Percent of discount
673
	 * @var float
674
	 */
675
	public $remise_percent;
676
677
	/**
678
	 * Total amount before taxes
679
	 * @var float
680
	 */
681
	public $total_ht;
682
683
	/**
684
	 * Total VAT amount
685
	 * @var float
686
	 */
687
	public $total_tva;
688
689
	/**
690
	 * Total local tax 1 amount
691
	 * @var float
692
	 */
693
	public $total_localtax1;
694
695
	/**
696
	 * Total local tax 2 amount
697
	 * @var float
698
	 */
699
	public $total_localtax2;
700
701
	/**
702
	 * Total amount with taxes
703
	 * @var float
704
	 */
705
	public $total_ttc;
706
707
	/**
708
	 * Liste d'options cumulables:
709
	 * Bit 0:	0 si TVA normal - 1 si TVA NPR
710
	 * Bit 1:	0 si ligne normal - 1 si bit discount (link to line into llx_remise_except)
711
	 * @var int
712
	 */
713
	public $info_bits = 0;
714
715
	/**
716
	 *  Constructor
717
	 *
718
	 *  @param	DoliDB		$db		Database handler
719
	 */
720
	public function __construct(DoliDB $db)
721
	{
722
		$this->db = $db;
723
	}
724
}
725
726