C#: Increment invoice number based on set cycles - c#

I am creating incrementing invoice numbers, like so: AABBBB1122.
'A' and 'B' are bound to identifiers in my code.
But the digits I need to be month and year, respectively.
For example: 0821 (august, 2021).
I don't want to connect it to a calendar in any way.
If possible I would like to define a starting date, and increment from there.
That is: 0821 would have to be incremented to 0921, 1021, 1121, 1221 -
before the year is incremented as well; 0122.
How can I do that?
What I've got so far:
string AA {
get { return this.IdentifierA.Substring(0, 2);
set { SetAndNotify(ref this.AA, value); }
}
string BB {
get { return this.IdentifierB.Substring(0, 4);
set { SetAndNotify(ref this.BB, value); }
}
string InvoiceNumber {
get { return String.Concat(AA + BB + /* what goes here? */).ToUpper(); }
set { SetAndNotify(ref this.InvoiceNumber, value);

Sounds like a peculiar way to do invoice numbers.. You are saying you don't want it based on the current date, but to just increment in a MMYY style?
Well given a typical auto-increment int KEY, which goes up by 1 for each invoice, use:
((KEY % 12).ToString("00")+(KEY/12).ToString("00"))
Start KEY at 12*21+8 to start with 0821.
.. But based on the invoice requirement I think what you must surely be asking for is:
(DateTime.Now.Month.ToString("00")+DateTime.Now.Year.ToString("00"))

This would be:
return string.Format("{0}{1}{2}", AA, BB, DateTime.Now.ToString("MMyy")).ToUpper();
or in new concatenation
return $"{AA}{BB}{DateTime.Now.ToString("MMyy")}".ToUpper();

If I understood correctly your question, the objective is to parse the date from the string and generate the next id. You can then use the following logic.
string id = "AAAA1221";
// Extract data.
int i = Convert.ToInt32(id.Substring(id.Length - 4, 4));
int year = i % 100;
int month = i / 100;
// Perform increment logic.
if (month == 12)
{
year++;
month = 1;
}
else
{
month++;
}
// Reformat key
string newId = $"{id.Substring(0, id.Length - 4)}{month:D2}{year:D2}";
In the case you don't want to use the previous id, you can just start the same logic with your previous value in the 'i' variable.

Related

How do I merge adjacent time spans with the same data value?

I have a file with comma separated values (CSV), that have this format:
26/09/2015,GROUP_1,0,0,0,0,0,0,0,0,0,0,12345.006,12345.006,27469.005,27469.005,27983.005,27983.005,28081.005,0,0,0,28105.005,28105.005,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
Every number represents the work hours over an interval of 15 minutes, over the range 8:00 am - 8:00 pm. The first start time is 08:00:00) and the last start time will be 19:45:00; there are 49 "columns" of data.
0,0,0,0,0,0,0,0,0,0,12345.006,12345.006,27469.005,27469.005,27983.005,27983.005,28081.005,0,0,0,28105.005,28105.005,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
The date will be the date when the "event" happened and it's the date on the data. But I need to get the values that are the same and specify a time range. For example, those first two non-zero values are the same:
12345.006,12345.006
These start at 10:30 and 10:45; I need to merge these and report 12345 hours for the time span 10:30 - 11:00 am.
I read the file; I have those values as an array, and the problem I'm having is how to "group" the same values into the appropriate time ranges.
DateTime startDate = new DateTime(2015,08,05);
DateTime finisDahte = new DateTime(2015,08,05);
int column = 0;
for (int i = 0; i < data.Length; i++)
{
//timerange start with every 15 minutes by column
if (column >= 2)
{
if (data[i] != "0")
{
//Getting rid of decimals, they are not neccesary and that's how the file have it, I dont know why
if (data[i].Contains('.'))
{
data[i] = data[i].Substring(0, data[i].LastIndexOf('.'));
}
//we check if there is a next index to compare the same value
if ((i + 1) <= totalElementos)
{
var nextElem = data[i + 1];
if (nextElem != "0")
{
nextElem.Substring(0, nextElem.LastIndexOf('.'));
}
else
{
//the is no next element... something here
}
//CRUCIAL PART: if the current index it's the same as the next one, it means they share the time range
if (data[i] == nextElem)
{
//the same index as the next one
//I need to identify when it's the first time I'm comparing a value with the next one, so I can set a start date
//I need to sum the total amount of time ranges for every repetition they have and save when the value start and when the value is different (so it's a new value)
}
else
{
//it's not the same index, so technically the finishdate will be set here?
}
}
else
{
//there is not more indexes, so finishdate will be here
}
}
}
//column++;
}
Hope I could explain. Thanks
You need to generate a class like the code below. I changed the date to US format for testing. The code below reads from a string using StringReader and when reading from a file use StreamReader instead.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
List<DataSample> samples = new List<DataSample>();
string data = "9/26/2015,GROUP_1,0,0,0,0,0,0,0,0,0,0,12345.006,12345.006,27469.005,27469.005,27983.005,27983.005,28081.005," +
"0,0,0,28105.005,28105.005,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0\n";
StringReader reader = new StringReader(data);
string inputline = "";
while ((inputline = reader.ReadLine()) != null)
{
string[] dataArray = inputline.Split(new char[] { ',' });
DateTime startDate = DateTime.Parse(dataArray[0]);
startDate = startDate.AddHours(8);
DateTime timeCounter = startDate;
string groupName = dataArray[1];
for (int i = 2; i < dataArray.Length; i++)
{
if (dataArray[i] != "0")
{
DataSample newSample = new DataSample();
samples.Add(newSample);
newSample.name = groupName;
newSample.time = timeCounter;
newSample.value = double.Parse(dataArray[i]);
}
timeCounter = timeCounter.AddMinutes(15);
}
}
var groupByValue = samples.AsEnumerable()
.GroupBy(x => x.value)
.ToList();
foreach (var group in groupByValue)
{
Console.WriteLine("Value : {0}, Times : {1}", group.Key.ToString(), string.Join(",",group.Select(x => x.time.ToString())));
}
Console.ReadLine();
}
}
public class DataSample
{
public string name { get; set; }
public DateTime time { get; set; }
public double value { get; set; }
}
}
​
This is not a coding service; you need to go a little farther. You've done a good job of outlining your algorithm; now, you should put in a few print statements to track the operation of your code. Do the loops and if statements give you the control flow you expected? A good way to do this is to put your comments into print statements, such as in your last inside comment:
print "there is not more indexes, so finishdate will be here"
Also print the loop index, values you found, etc.
Once you have corrected any flow problems there, start filling in the comment-only blocks with useful code, a few lines or one block at a time.
When you hit a specific problem, post your code and the actual output. That's where StackOverflow is designed to help you.
In the meantime, I'd like to g a change to your outer loop. Let it be driven as a while loop, so you can freely advance your index as needed. Right now, you're keeping two variables for almost the same purpose: i and column. Instead, use just one, something like:
column = 0
while (column < data.Length)
// Find all of the indices with the same consecutive value
finish_index = column;
while data[start_column] = data[finish_index+1]
finish_index++;
// You now have the range of work slots to merge.
printf "Time slots %d - %d have %d work hours", column, finish_index, int(data[column])
column = finish_index+1
}
You will still have to convert column numbers to times: 15 minutes * column + 8:00am. I've also left out a few intermediate nice steps, but I think that you already have them in your comments.
Does this get you moving?

storing user input into an array and referring to the array c#

This code works properly well but it doesn't have a memory for storing user input.
I also need to categorize the grades into its respective columns in array format, having "S/N, Category and Count" and I dont know how to go about it. Any help would be appreciated.
namespace ExamScore
{
class YourExamScore
{
private static string GetGrade(int examScore)
{
if (examScore >= 90 && examScore <= 100)
return "Excellent";
if (examScore >= 70 && examScore <= 89)
return "Good";
if (examScore >= 50 && examScore <= 69)
return "Satisfactory";
if (examScore >= 0 && examScore <= 49)
return "Unsatisfactory";
return "Invalid";
}
static void Main(string[] args)
{
// Print a greeting message. After all, why not?
Console.WriteLine("Welcome to ExamScore Calculator!");
Console.WriteLine("Input Your Exam Score...");
Console.WriteLine("Press -2 when you have inputed all scores");
while (true)
{
var examScore = Convert.ToInt32(Console.ReadLine());
if (examScore == -2)
{
break;
}
var grade = GetGrade(examScore);
Console.WriteLine(grade);
}
Console.WriteLine("\n\nProccessing Scores... Please Wait...");
Console.ReadLine();
}
}
}
Well it seems like a homework, so i am not going to help with codes directly,
for the storage of marks, you can follow either of one way
if you know how many input you are going to use, then use a fixed size array
var marks = new int[10];
if the no. of input is not fixed then you can use List<int>
var marks = new List();
I am not sure if I understood you correctly, but I think you are asking to keep track of the amount of grades that fall in a certain category. In that case, I would do it as follows.
You will need to keep track of an array of ints (int[]) with an entry for each category that keeps track of the amount of grades in that category so far. So you will first have to make a static array of size 4, which will automatically initialize with zeroes everywhere. Semantically, each index stands for a category (say 0 for excellent, 1 for good, etc.). Then, in the GetGrade method you should for each category also increase the right category before returning.
To store user input in memory you can use array or list (List<MyData> is better if you have to insert often, Hashtable if you want to search fast, MyData[] - least memory and fastest access by index, etc.)
After entering -2 you may want to store (serialize) your data somehow, so data will persists between runs. It can be xml-file (XmlSerializer), own format text-/binary- file, database, etc.
You also need to define how you will work with data: are they saved immediately after entering (then database itself is a memory storage) or upon exit, delete/correct possibility and such.
Finally, you can add statistic and reporting, which query your data and produce results.
It is a bit unclear what
"S/N, Category and Count"
stands for. Could you elaborate? Your code is asking for score and producing grade. You could count how many users have which grade, how many total users, how many grades (all those are "Counts"), but "S/N" and "Category" are confusing.
To example, you only want to print how many users belongs different grades.
1) Very manual approach, count occurrences (statistic) upon entering (use)
int grade90plus; // fields
int grade70plus;
...
// inside GetGrade
if(examScore > 90)
{
grade90plus++;
return "Excellent";
}
else
if(examScore > 70)
{
grade70plus++;
return "Good";
}
...
// report
Console.WriteLine("Excellent: " + grade90plus);
Console.WriteLine("Good: " + grade90plus);
2) High tec (serialization unfriendly)
public class Grade
{
public string Name;
public int Count;
public int ScoreMin;
public int ScoreMax;
public bool Test(int score) { return score >= ScoreMin && score <= ScoreMax; }
public static readonly Grade Excellent = new Grade() {Name = "Excellent", ScoreMin = 90, ScoreMax = 100};
public static readonly Grade Good = new Grade() {Name = "Good", ScoreMin = 70, ScoreMax = 89};
public static readonly Grade[] GetAll = new Grade[] { Excellent, Good };
}
private static string GetGrade(int examScore)
{
foreach(var grade in Grade.GetAll)
if(grade.Test)
{
grade.Count++;
return grade.Name;
}
return "Invalid";
}
// report
foreach(var grade in Grade.GetAll)
Console.WriteLine(grade.Name + "," + grade.Count);
You can surely improve those or make own solution (serialization friendly, utilizing database, one what uses expressions for Test, etc).

Algorithm for finding the next number without two or more consecutive 6s

I have been given a rather odd requirement to fulfill for a particular solution. The requirement is to write a function given the current number, to find the next consecutive number that excludes numbers with two or more consecutive 6s.
So far I have the following code (in C#) which I tested with a few inputs and it works. I know it's not the most efficient solution but it does the job, I just want to see if there's a more efficient way of doing this. The way I go about it is by converting the number into a string and using a simple regular expression to see if the next sequence is a valid one given the requirement. Also I am aware that it will throw a error once the number reaches its (2^31) - 1 limit, but at the moment that's not an issue.
public int GetNextSequenceNumber(int currentSequenceNumber)
{
var nextSequenceCandidate = currentSequenceNumber + 1;
var strNum = nextSequenceCandidate.ToString();
if (IsValidSequenceNumber(strNum))
{
return nextSequenceCandidate;
}
else
{
do
{
strNum = (++nextSequenceCandidate).ToString();
} while (!IsValidSequenceNumber(strNum));
return nextSequenceCandidate;
}
}
private bool IsValidSequenceNumber(string sequenceNumber)
{
return !Regex.IsMatch(sequenceNumber, "[6]{2,}");
}
I'm thinking there's another way where one would use division and the modulus operation to find out the digits position and increment that just as needed. Any input is appreciated, thanks!
The most efficient solution that I see would actually use string replacement, but this works only if you are incrementing and returning all values of the sequence. Just replace 66 by 67.
If any starting number is allowed, you will have to append just as many 0s as there were digits after the first occurrence of 66 in your number string.
Convert your number to decimal format, say to a byte array, scan for 66.
If you can't find it, your are done.
Other wise, change it to 67, followed by all zeros.
I think you best bet is to not think in terms of incrementing a number, but in terms of validating a string.
Something like the following would prevent potentially long loop runs, and should provide the next lowest possible value that doesn't have "66".
public static int GetNextSequenceNumber(int currentSequenceNumber)
{
int nextNumber = currentSequenceNumber += 1;
string nextNumberStr = nextNumber.ToString();
if (!nextNumberStr.Contains("66"))
{
return nextNumber;
}
else
{
//travel from left to right, find the 66, and increment the last 6 and reset the remaining values.
bool doreset = false;
bool lastwassix = false;
string newString = string.Empty;
for (int i = 0; i < nextNumberStr.Length; i++)
{
if (doreset) { newString += '0'; continue; }
char c = nextNumberStr[i];
if (c == '6')
{
if (lastwassix)
{
newString += '7';
doreset = true;
continue;
}
lastwassix = true;
}
newString += c;
}
return Convert.ToInt32(newString);
}
}

Convert number to percentage of random

I am looking at having a option to specify how often something should run, in terms of a percentage.
So I would have a textbox, that takes a number 0-100 to detirmine a percentage. I want to then take this number, and convert in into a matching random statement.
So for example:
textPercentage.text = "10"
would be changed to something like:
if (rnd.Next(1, 11) = 1)
{
do();
}
What is the best way to convert that numerical value to a matching random statement?
var i = int.Parse(textPercentage.Text);
if (rnd.Next(1, 101) <= i)
{
do();
}
You might want to use int.TryParse in stead of int.Parse to gracefully handle non digit inputs.
First get your value back into an int.
In WPF, this is easy because you just databind to an int property.
In WinForms you use the int.Parse method to get the value back: int.Parse(textBox.Text)
If you use the values 0-100 you can use something similar to your original code, as-is.
int value = int.Parse(textBox.Text);
if(rnd.Next(0, 100) < value)
{
DoSomething();
}
Otherwise, just add one to both the start and end values:
int value = int.Parse(textBox.Text);
if(rnd.Next(1, 101) <= value)
{
DoSomething();
}
The first one is simpler, so I'd go with it :)

formatting decimal value in column in datagridview

I want to formatting a column of sum value: for example I have 19240 I want it 192.40 I tried many way but nothing
dgw.Grid.Columns["Sum"].DefaultCellStyle.Format="0:00" // 192:40
dgw.Grid.Columns["Sum"].DefaultCellStyle.Format="n" // 19240.00
dgw.Grid.Columns["Sum"].DefaultCellStyle.Format="0.00" // 19240.00
dgw.Grid.Columns["Sum"].DefaultCellStyle.Format=string.Format("{0:0.##}") // Exception
I want all items in that column in datagrid view with the same format
edit : a way to do it:
I use this methode to get what I want
int _sum;
public int Sum
{
get { return _sum; }
set { _sum= value; }
}
public double SumAsdouble
{
get
{
if (_sum== 0)
return 0;
else
return Convert.ToDouble(_sum) / 100;
}
}
and I make hidden the Sum and show the SumAsdouble
and this work for me
What is the value in the cell?
It looks like it is 19240, which means that formatting it to 2DP will return 19240.00, as you are seeing. If you want 192.40, then you'll need to divide by 100 first - you'll not be able to do this with string formats.
I assume that you always want to have the last two digits to be decimal digits and that your number is always five digits long. Then you can use "000.00" or "%" as format string. Alternatively, you can devide your number by 100.

Categories

Resources