PHP invoice number with alpha numeric increment - php

The invoice number looks like this INV{Year}{Month}-1A Ex: INV202201-1A
The suffix 1A's numerical value can only go up to 9. Then the Alphabet should change to B, then the numerical value again goes to 9B then has to change to C & so on. Also when the month changes the prefix should change back to 1A. I've already tried adding all alphabets to an array but I can't seem to figure it out.
I would really appreciate it if someone can point me out the right logic for this.

The total of 1A to 9Z is 234.
The data for $usedPostfix should be from the database or stored somewhere else.
$usedPostfix = array(
"202112" => 220,
"202201" => 10,
"202202" => 0,
);
This is where we find the unused postfix to be returned.
$date = date_format(new DateTime(), 'Ym');
function getInvoice($counter, $date)
{
foreach (range('A', 'Z') as $char) {
for ($i = 1; $i < 10; $i++) {
if ($counter > 0) {
$counter--;
continue;
}
return "INV" . $date . "-" . $i . $char;
}
}
}
$date is using Ym format. (e.g: 202201)
Examples:
Using $date
echo getInvoice($usedPostfix[$date], $date); // OUTPUT: INV202201-2B
Using custom $date (* for example purpose)
echo getInvoice($usedPostfix["202112"], $date); // OUTPUT: INV202201-5Y
echo getInvoice($usedPostfix["202202"], $date); // OUTPUT: INV202201-1A

Related

calculating no of days in given month

I have the LatestValuesDate 'Apr','May','Jun'.I have array with no of days for the months.
If LatestValuesDate is may I want to show the number of days =61. if LatestValuesDate is Jul I want to show the number of days =91
but now I got only 31
$month_days=array("Apr"=>"30", "May"=>"31", "Jun"=>"30", "Jul"=>"31", "Aug"=>"31", "Sep"=>"30", "Oct"=>"31", "Nov"=>"30", "Dec"=>"31", "Jan"=>"31", "Feb"=>"28", "Mar"=>"30");
$val='May';
$days1=0;
$noOfDays=$days1+$month_days[$val];
For your expected result, you would need to loop through the array and sum the values into a variable. But you will have to stop the loop after adding the value according to your $val variable. You can achieve that using the following code:
$month_days=array("Apr"=>"30", "May"=>"31", "Jun"=>"30", "Jul"=>"31", "Aug"=>"31", "Sep"=>"30", "Oct"=>"31", "Nov"=>"30", "Dec"=>"31", "Jan"=>"31", "Feb"=>"28", "Mar"=>"30");
$noOfDays = 0;
$val='May';
foreach($month_days as $key=>$value){
$noOfDays = $noOfDays + $value;
if($key == $val)
break;
}
echo $noOfDays;
Just if you don't want to use foreach():
$month_days=array("Apr"=>"30", "May"=>"31", "Jun"=>"30", "Jul"=>"31", "Aug"=>"31", "Sep"=>"30", "Oct"=>"31", "Nov"=>"30", "Dec"=>"31", "Jan"=>"31", "Feb"=>"28", "Mar"=>"30");
$val='May';
$noOfDay = 0;
$month_index = array_search($val, array_keys($month_days));
$noOfDay = array_sum(array_slice($month_days,0,$month_index +1));
var_dump($noOfDay);
Output: int(61)
NOTE: March has the wrong number of days in your array declaration, it should be 31.
I was always advised to not use break like commands in loops and to always have a "well defined loop" that doesn't require a break.
This uses a while loop and a flag. If the search is not found, the loop will end summing all values.
$month_days = ['Apr'=>'30', 'May'=>'31', 'Jun'=>'30', 'Jul'=>'31', 'Aug'=>'31', 'Sep'=>'30', 'Oct'=>'31', 'Nov'=>'30', 'Dec'=>'31', 'Jan'=>'31', 'Feb'=>'28', 'Mar'=>'31'];
$val = 'May';
$day_count = 0;
$i = 0;
$keys = array_keys($month_days);
$done = 0;
while (!$done)
{
$done = $keys[$i] == $val || $i >= count($month_days) ? 1 : 0;
$day_count += $month_days[$keys[$i]];
$i++;
}
echo 'Day Count: ' . $day_count . "\n";
echo 'i: ' . $i . "\n";

Calculate mean of a range of XML Elements in PHP

I'm trying to calculate the mean of a range of XML elements in PHP, but haven't found any solution yet.
Here are the XML elements.
<root>
<quoteDay>
<date>2018-02-26</date>
<close>1586,96</close>
</quoteDay>
<quoteDay>
<date>2018-02-23</date>
<close>1577,11</close>
</quoteDay>
<quoteDay>
<date>2018-02-22</date>
<close>1565,5</close>
</quoteDay>
</root>
Here is the PHP code:
<?php
$xml = simplexml_load_file("file.xml") or die("Error: Cannot create object");
$id = -1;
$total[] = 0;
foreach ($xml->root as $root) {
foreach ($root->quoteDay as $quoteDay) {
$id ++;
$total[] += $root->quoteDay[$id]->close;
$close = number_format(round($quoteDay->close,0));
echo $quoteDay->date; echo $close; echo $total[$id+1];
}
}
?>
So, for each quoteDay, I would like to return the date, close and a moving average.
Date 2018-02-26 would return the mean of "close" for 2018-02-26 and 2018-02-23 = (1586,96+1577,11)/2.
Mean for 2018-02-23 would return (1577,11+1565,5)/2.
I've, as you can see, tried to sum a cumulative total sum for each element, but for some reason I can't understand it won't work.
How can I accomplish calculating a moving average for the elements?
In order to achieve your result you need to do a couple of things:
simplexml_load_file() already gives you the root, so there's no need for your first loop
The $total array is not necessary
Your XML has , as decimal separators, but PHP uses ., so you need to replace them in order to do math and not lose the decimals (here I cast to float which can make you lose precision, look into bcmath to avoid that)
I assume that for the first day, when there's no previous, the moving average is the day's value
So, your code would look like this:
<?php
$xml = simplexml_load_file("a.xml") or die("Error: Cannot create object");
$id = 0;
foreach ($xml->quoteDay as $quoteDay) {
echo "Moving average for ".$quoteDay->date.":".PHP_EOL;
$current = (float) str_replace(",", ".", $quoteDay->close);
$previous = $xml->quoteDay[$id + 1]
? (float) str_replace(",", ".", $xml->quoteDay[$id + 1]->close)
: $current;
$movingMean = ($current + $previous) / 2;
echo $movingMean.PHP_EOL;
echo PHP_EOL;
$id++;
}
Demo
Result
Moving average for 2018-02-26:
1582.035
Moving average for 2018-02-23:
1571.305
Moving average for 2018-02-22:
1565.5
To generalize it to $daysInMovingMean days, use a for loop to get the days up to the days needed, stopping earlier if necessary (i.e. no more days left):
$xml = simplexml_load_file("a.xml") or die("Error: Cannot create object");
$id = 0;
$daysInMovingMean = 3;
foreach ($xml->quoteDay as $quoteDay) {
echo "Moving average for ".$quoteDay->date.":".PHP_EOL;
$sum = 0;
for ($days = 0; $days < $daysInMovingMean; $days++) {
if (!$xml->quoteDay[$id + $days]) break;
$sum += (float) str_replace(",", ".", $xml->quoteDay[$id + $days]->close);
}
$sumovingMean = $sum / $days;
echo $sumovingMean.PHP_EOL;
echo PHP_EOL;
$id++;
}
Notice that in this example you get the same results as before if you set $daysInMovingMean = 2;
Demo

Getting nth-child with an offset in PHP with modulus

When iterating through a loop, I've used the modulus operator in an if statement to obtain nth results pretty easily like this:
// Get 5th item in series
if ($variable->array_item % 5 == 0) {
echo $variable->array_item;
}
How do you do get the 5th item in a series with an offset of 3 (ie, 3,8,13,18,23,etc.)?
I've seen a couple of methods but I'm looking for a canonical answer and I really don't see one on S.O. right now.
Your code specifically requests numbers that are evenly divisible by 5, whereas what you want are numbers 3, 8, 13, 18, 23, etc. This is easy enough to do using almost identical code to what you have:
// set up some test data
$testArray = [];
for ($i = 1; $i <= 30; ++$i) {
$testArray[$i] = "Testing {$i}";
}
// here's where the actual work happens
foreach ($testArray as $key => $value) {
if ($key % 5 == 3) { // <-- note 3, not 0
echo "Found $value\n";
}
}
$iterator = 0;
$step = 3;
while($iterator < count($collection))
{
echo $collection[$iterator]
$iterator += $step
}

How do i name my files 00, 01, 02 and so on in PHP

I have some files i want to upload. And when i upload them, i want the first to be numbered 00, the second to be numbered 01. I wrote a function to get the newest number, but of course, this ain't workin
$i = 00;
while($i < $filecount)
{
if(!file_exists("../photo/$i.jpg"))
{
return $i;
print($i);
}
else if(file_exists("../photo/$i.jpg")) {
$i = $i + 01;
}
But now, when i upload a file, it just numbers it: 0, 1, 2, 3.
I got some new code :
function getFileNumber() {
$i = 0;
while ($i < $filecount) {
$fn = sprintf("../photo/%02d.jpg", $i);
if (!file_exists($fn)) {
return $i;
} else {
$i ++;
}
}
}
But it returns 00 every time, but i want to to go 00,01,02,03. Somebody any ideas?
Integer literals don't contain any formatting. 1 is the value one, it can't be "01", that's a formatting problem. As such, format your numbers:
$file = sprintf('%2u.jpg', $i);
You can use
echo str_pad($i, 2, "0", STR_PAD_LEFT);
Additionally, your print() after the return will never be executed and use if/else instead of if/elseif.
Check out sprintf() that allows for pretty flexible string formatting - especially check out the padding and width specifiers. You're looking for something along the lines of
$i = 0;
while ($i < $filecount)
{
$fn = sprintf("../photo/%02d.jpg", $i);
if (!file_exists($fn))
{
return $i;
}
else {
$i++;
}
}
print($i) is a dead code - it will never get executed
Second, you don't need to check else if(file_exists("../photo/$i.jpg")) again.
00 is not valid integer, it's simplified to just 0, as 01 simplified to 1 (actually it's treated as octal digits, so 08 and 09 is invalid)
So, to have the real 00, 01, 02 you'll have to format your output with spritf
$q=0;
$file_suffix = sprintf("%02d", $q);
if(!file_exists("../photo/{$file_suffix}.jpg")){
print($i);
return $i;
}
else{
$q+=1;
}

PHP: Next Available Value in an Array, starting with a non-indexed value

I've been stumped on this PHP issue for about a day now. Basically, we have an array of hours formatted in 24-hour format, and an arbitrary value ($hour) (also a 24-hour). The problem is, we need to take $hour, and get the next available value in the array, starting with the value that immediately proceeds $hour.
The array might look something like:
$goodHours = array('8,9,10,11,12,19,20,21).
Then the hour value might be:
$hour = 14;
So, we need some way to know that 19 is the next best time. Additionally, we might also need to get the second, third, or fourth (etc) available value.
The issue seems to be that because 14 isn't a value in the array, there is not index to reference that would let us increment to the next value.
To make things simpler, I've taken $goodHours and repeated the values several times just so I don't have to deal with going back to the start (maybe not the best way to do it, but a quick fix).
I have a feeling this is something simple I'm missing, but I would be so appreciative if anyone could shed some light.
Erik
You could use a for loop to iterate over the array, until you find the first that is greater than the one you're searching :
$goodHours = array(8,9,10,11,12,19,20,21);
$hour = 14;
$length = count($goodHours);
for ($i = 0 ; $i < $length ; $i++) {
if ($goodHours[$i] >= $hour) {
echo "$i => {$goodHours[$i]}";
break;
}
}
Would give you :
5 => 19
And, to get the item you were searching for, and some after that one, you could use something like this :
$goodHours = array(8,9,10,11,12,19,20,21);
$hour = 14;
$numToFind = 2;
$firstIndex = -1;
$length = count($goodHours);
for ($i = 0 ; $i < $length ; $i++) {
if ($goodHours[$i] >= $hour) {
$firstIndex = $i;
break;
}
}
if ($firstIndex >= 0) {
$nbDisplayed = 0;
for ($i=$firstIndex ; $i<$length && $nbDisplayed<$numToFind ; $i++, $nbDisplayed++) {
echo "$i => {$goodHours[$i]}<br />";
}
}
Which would give you the following output :
5 => 19
6 => 20
Basically, here, the idea is to :
advance in the array, until you find the first item that is >= to what you are looking for
get out of that first loop, when found
If a matching item was found
loop over the array, until either its end,
or you've found as many items as you were looking for.
You can also use the SPL FilterIterator. Though it's not the fastest solution there is, it has the advantage that you can "prepare" the iterator somewhere/anywhere and then pass it to a function/method that doesn't have to know how the iterator works on the inside, i.e. you could pass a completely different iterator the next time.
class GreaterThanFilterIterator extends FilterIterator {
protected $threshold;
public function __construct($threshold, Iterator $it) {
$this->threshold = $threshold;
parent::__construct($it);
}
public function accept() {
return $this->threshold < parent::current();
}
}
function doSomething($it) {
// no knowledge of the FilterIterator here
foreach($it as $v) {
echo $v, "\n";
}
}
$goodHours = array(8,9,10,11,12,19,20,21);
$it = new GreaterThanFilterIterator(14, new ArrayIterator($goodHours));
doSomething($it);
prints
19
20
21
As $goodHours is already sorted, that's something easy:
$next = 0;
foreach($goodHours as $test)
if($test > $hour && $next = $test)
break;
After that four-liner (that can be written in a smaller number of lines naturally), $next is either 0 if $hour could not be matched in $goodHours or it contains the value that immediately proceeds $hour. That is what you asked for.
This only works when $goodHours is sorted, in case it's not, you can sort it by using the asort() function.
Try this function:
function nextValueGreaterThan($haystack, $needle, $n=1) {
sort($haystack);
foreach ($haystack as $val) {
if ($val >= $needle) {
$n--;
if ($n <= 0) {
return $val;
}
}
}
}
$goodHours = array(8,9,10,11,12,19,20,21);
echo nextValueGreaterThan($goodHours, 14); // 19
echo nextValueGreaterThan($goodHours, 14, 3); // 21
Here's an answer similar to the rest of these, including an optional "offset" parameter, that gets your n'th item past the de-facto first one.
class GoodHours {
private $hours = array(8,9,10,11,12,19,20,21);
public function getGoodHour($hour, $offset = 0) {
$length = count($this->hours);
for ($i = 0 ; $i < $length && $this->hours[$i] < $hour ; $i++)
; // do nothing
return $this->hours[($i + $offset) % $length];
}
}
// some test values
$good = new GoodHours();
$x = $good->getGoodHour(5); // 8
$x = $good->getGoodHour(5,1); // 9
$x = $good->getGoodHour(5,2); // 10
$x = $good->getGoodHour(10); // 10
$x = $good->getGoodHour(10,1); // 11
$x = $good->getGoodHour(10,2); // 12
$x = $good->getGoodHour(21); // 21
$x = $good->getGoodHour(21,1); // 8
$x = $good->getGoodHour(21,2); // 9
$x = $good->getGoodHour(21); // 8
$x = $good->getGoodHour(22,1); // 9
$x = $good->getGoodHour(22,2); // 10

Categories