As an imaginary business owner I learn new things every day. Did you know, for example, that you need to pay the people who work for you? This is what this entire module is all about - the payroll and how we implement it with 1C:Enterprise.
First, let’s see what kind of task we have in front of us. This is how one working day looked like for my favorite cashier - Agnes Blue. She worked the whole nine-hours shift from nine AM to six PM. It was pretty much behind-the-counter work with some occasional shelves tidying-up - usual shift that is paid $15 dollars per hour according to our standard contract. During this time she had a 45 minutes lunch break, which, as we agreed, should be paid at a little smaller hourly rate of $12 dollars per hour.
My second-favorite cashier - Danny - came up late but worked a long 11-hour shift, that included a three-hour overtime rated 18 dollars per hour and a delivery truck unloading - physically challenging part of Danny’s duties rated at whooping 20 dollars per hour.
So, my task here is to collect all this information for all my workers over a period of time and calculate wages taking all these work overlaps and different hourly rates into account. Let’s see what Metadata Objects we need to make this work.
First of all we need some place to keep all the different work types we’ve got. We also need a way of telling what should happen when different work types overlap each other like in these cases. During these three periods of time the normal working shift (rated at $12 per hour) was interrupted by differently rated types of work, so we need to know what rate to use here. We say that some works displace parts of other works, and we need a table looking somewhat like this to keep records of what displaces what. Here we can say that the normal working shift gets displaced by the lunch as well as the overtime work and the truck unloading.
We’re also gonna need some place to store the info on who did what kind of work and when. As soon as we know which type of work displaces which and have primary data about the time worked we can calculate the actual time worked - the table that breaks down, for example, this single record into these two records because some part of it was displaced by the lunch.
So, this is our single-day picture. Now let’s go and implement it.
To store different types of earnings that impact workers’ wage as well as various dependencies between them we use a Metadata Class called a Chart of Calculation Types. Its generic name implies that you can use these guys for any kind of recurrent calculations, but its most common use is for payroll purposes.
So, I’m creating a new metadata object of this class. Naming it WorkTypes and these are its standard attributes. Here is our reference and our description. The only thing missing is the hourly rate. So, I’m adding a new attribute here like this. And this is it.
As for this displacement dependencies, here is a standard tabular section they live in. As you can see it’s disabled for now, and here is the reason for that. This setting over here tells the Chart whether its earnings are based on some period of time (like wages dependent on hours worked) or just some momentary payments (like bonuses and stuff like that). We are looking at the time period dependent earnings, so, I’m turning this back on and now the displacement tabular section is enabled. Why? Because momentary earnings don’t have a time period and cannot compete over it with each other. But time-dependent earnings can.
All right. Now, to this part of our diagram. To store the primary data about the time worked and to get this data corrected according to displacements, we use another Metadata Class called the Calculation Register. So, I’m creating a new register and calling it Payroll, like this. This setting over here tells the register what Chart of Calculations to use. So, I’m selecting our WorkTypes chart here.
Next. This setting is a calculation register counterpart of this chart setting over here. It tells the register the same thing this setting tells the chart: that its records should belong to the period of time rather than to the moment of time. This is definitely our case, so, I’m switching it on like this, and the register grows itself these two standard attributes to store the beginning and the end of a period.
OK. Back to main settings. Whenever this period flag is turned on, the register requires us to specify this set of three schedule settings. The schedule helps the register to tell days of different types from each other. For example, there might be working days, holidays, half days, etc. We are not using this functionality yet, but we have to make these settings anyway. So, I’m creating a new information register called Schedule, then adding a Date dimension and a Number resource. Now, I need to set this new register as a schedule for my calculation register like this.
Now to the data structure. As it is the case for all Platform registers, the calculation register has dimensions, resources and attributes. The meaning of a dimension is very different from what we saw in accumulation and information registers. Let’s bring back our working hours diagram and see what impact dimensions have on the displacement mechanism.
Having no dimensions like this means that there is no boundary between times worked by different cashiers. So, when the lunch starts displacing things, it cuts off pieces of both working shifts, like this. And the same happens for the truck unloading piece.
But as soon as I add the Cashier dimension, data belonging to different cashiers gets separated and the displacements get confined within these separated areas. So, Agnes’s lunch stops messing up with Daniel’s working hours, and Daniels truck uploading gets off Agnes’s shift.
OK, back to the Designer.
This Cashier dimension is, of course, the Cashier Catalog reference type. We don’t need any additional fields in the register so far, but we could use a good look at the register’s insides. So I’m adding a new list form like this. This is the form’s dynamic list, and this is where it takes its data from.
Let’s see what else we’ve got here. So. These are all the tables the register has, and this is what they do. This first table stores the raw data we write to the register. In other words, it’s our TimeWorked table in the flesh. And this second table shows us the same data corrected for displacements. So, this is our ActualTimeWorked table, which is exactly what I want to see in this form. So, I’m selecting it instead of the first table and re-dropping it to the form like this.
Now, let’s get to the fields we want to see. First of all, I need a WorkType - the reference to this WorkTypes list that I could use to get the work’s name. If we look at the register’s standard attributes, this is our reference. And it got here because we set the WorkTypes chart as a chart of calculations for this register over here. So, I’m dropping this field to the list, adding the Cashier field, which is our Cashier dimension value.
And then I need these displacement-adjusted start and finish fields, which are calculated by the register and live in these two fields over here.
OK. Tidying it up a little bit and we’re done here.
One more thing I need is a document that registers time worked. So, I added one behind the scenes and this is what’s inside.
We have a reference to the type of work done by this specific cashier during this period of time. We also want this document to post its data to this calculation register. So, I implemented these few lines of fairly simple code in the document’s Posting event handler.
And this is what’s going on here. First, we’re turning on this flag, telling the document to write data to the calculation register. Then we’re creating a new register record, writing down the time when the record was made and then just filling out all the fields.
And one last thing I did was this Payroll subsystem I included all these recent objects to.
OK, let’s see how it works. This is our new subsystem, And the first thing I want to do is fill out the Work types chart. So, here it goes. We also need to tell the Working Shift that it gets displaced by the rest of the work types. So, I’m opening the Working Shift, and here is the standard tabular section intended for this purpose. So, I’m filling it out like this, and we’re done here.
Now let’s go and register hours worked. And here they go just like this. Now we can open this virtual table and see what this data looks like after the displacement adjustments. So, I’m going here, and here is the Agnes’s working shift broken down into two pieces by this lunch time.
And this is Dany’s shift also broken down by truck unloading, as well as trimmed from the end by this overtime work. So, this is our time worked corrected according to the displacement settings. The only thing left to do is to grab this data and calculate wages.
And this is how I did it. I added a new document called Payroll and here is its data structure. The Start and the Finish attributes are for me to set the period of time I’m calculating the wages for. And this tabular section down here is for storing the Cashier reference and their wage for the period.
To run the calculation process and check out its results I created this form right here and added this Fill button to the command bar. When clicked this button runs this form command, that lives down here and runs this client procedure which in turn runs this server procedure to get the data access.
And this is what’s going on here on the server side. Let me lose this displacement tabular section first, so it doesn’t block the view. So, here is the query that calculates all the wages in one bite. Let me show you how. It goes to this calculation register’s table and reads all the displacement-adjusted records falling into this time interval.
Then it calculates the duration of the interval in minutes using this DATEDIFF function of the 1C query language and converts it to hours. After that it performs this hidden LEFT JOIN that takes the current record’s WorkType reference and goes all the way to the WorkTypes chart to look up the hourly rate to use it as a multiplier for the hours worked.
And finally, the query groups these records by Cashiers and sums up all the wages for each of them. The rest is easy: setting the parameters, running the query and filling out the document’s tabular section. And this is it.
Let’s check if it works. This is where our Payroll documents live. Creating a new one. Selecting the current week as a payroll interval. Running our query. And this is how much we owe to Agnes and Dany-boy for this week. Perfect.