'From VisualWorks(R), Release 2.5 of September 26, 1995 on September 8, 1996 at 12:42:16 pm'! Object subclass: #EmployeeTransaction instanceVariableNames: 'date employee ' classVariableNames: '' poolDictionaries: '' category: 'Payroll'! EmployeeTransaction comment: 'I''m an abstract class of transactions that can be posted to an Employee. My most important method is postTo:, which is implemented by subclasses to send a message to the Employee to post the transaction. The Employee has all the information, so it has to be the one to implement posting, but the postTo: method implements a standard interface so that clients of the payroll system can always post a transaction by sending the postTransaction: message to an Employee. Variables date Date at which transaction occured. employee '! !EmployeeTransaction methodsFor: 'accessing'! date ^date! ! !EmployeeTransaction methodsFor: 'posting'! post employee postTransaction: self! postTo: anEmployee "double dispatching" self subclassResponsibility! ! Object subclass: #TaxRule instanceVariableNames: 'breakPoints rates ' classVariableNames: '' poolDictionaries: '' category: 'Payroll'! TaxRule comment: 'I represent a tax table. Given a yearly taxable income, I can give the tax rate for it. I store the tax table as a sequence of amounts, and the tax rate for that amount. For example, in 1994 the tax rate for a single person (schedule X) was $0 15% $22750 28% $55100 31% $115000 36% $250000 39.6% breakpoints rates '! !TaxRule methodsFor: 'initialize-release'! initialize breakPoints := SortedSequence new. rates := OrderedCollection new.! ! !TaxRule methodsFor: 'accessing'! breakPoint: aNumber rate: anotherNumber "Add an entry to the tax table" | index | index := breakPoints indexOfStartOfIntervalContaining: aNumber. breakPoints add: aNumber. rates add: anotherNumber beforeIndex: index + 1! rateFor: aNumber "Return the tax rate for someone whose taxable income is aNumber dollars." ^rates at: (breakPoints indexOfStartOfIntervalContaining: aNumber)! ! "-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "! TaxRule class instanceVariableNames: ''! !TaxRule class methodsFor: 'instance creation'! new ^super new initialize! ! SortedCollection variableSubclass: #SortedSequence instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Payroll'! SortedSequence comment: 'I add one method to SortedCollection. It would be just as easy to add the method directly to the class instead of making a subclass, but this makes it easier to see the new method. The method is indexOfStartOfIntervalContaining: anElement It returns the index of anElement or, if it is not present, the index of the largest element smaller than it. It is useful when you want to have a table that is sorted by keys and you want to look up in the table to find the nearest match to a key. '! !SortedSequence methodsFor: 'accessing'! indexOfStartOfIntervalContaining: anElement "Return the index of anElement or, if it is not present, of the index of the largest element smaller than it." self isEmpty ifTrue: [^0]. ^(self indexForInserting: anElement) - firstIndex! ! Object subclass: #PayrollSystem instanceVariableNames: 'employees paychecks companyName ' classVariableNames: '' poolDictionaries: '' category: 'Payroll'! PayrollSystem comment: 'I am the top-level of the payroll system. I keep track of the employees and can "run the payroll", i.e. write paychecks for the employees. To run the payroll, send the messages makePaychecksAt: paydate - to create the paycheck transactions printPaychecksOn: aStream - to print them out postPaychecks - to record them in employees Note that the employees are not marked with having been paid until the paychecks are posted, so just creating the transactions or even printing them is not enough. There are some tests on the class side. Variables employees paychecks holds paychecks that have been created until they have been posted'! !PayrollSystem methodsFor: 'initialize-release'! initialize employees := Dictionary new.! ! !PayrollSystem methodsFor: 'accessing'! addEmployee: anEmployee employees at: anEmployee name put: anEmployee! employeeNamed: aName ^employees at: aName! employeeNames ^employees keys asOrderedCollection! name ^companyName! name: aString companyName := aString! ! !PayrollSystem methodsFor: 'actions'! makePaychecksAt: aDate paychecks := employees collect: [:eachEmployee | eachEmployee makePaycheckAt: aDate]! paycheckString | aStream | aStream := WriteStream on: (String new: 20). self printPaychecksOn: aStream. ^aStream contents! postPaychecks paychecks do: [:each | each post]. paychecks := #()! printPaychecksOn: aStream paychecks do: [:each | each printOnCheckStream: aStream]! ! "-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "! PayrollSystem class instanceVariableNames: ''! !PayrollSystem class methodsFor: 'tests'! aLittleOvertime ^#( #(40 0) #(50 0) #(40 0) #(40 0))! employee: anEmployee hours: collection starting: aDate | timecardDay | timecardDay := aDate. collection do: [:each | anEmployee postTimeCardFor: timecardDay hoursWorked: each first vacation: each last. timecardDay := timecardDay addDays: 7]! normalMonth ^#( #(40 0) #(40 0) #(40 0) #(40 0))! normalMonthOf5 ^#( #(40 0) #(40 0) #(40 0) #(40 0) #(40 0))! test "PayrollSystem test" | payroll day1 ralph faith | day1 := Date newDay: 5 year: 1996. payroll := self new. payroll name: 'Johnson Software'. ralph := Employee new named: 'Ralph Johnson'. ralph changeSalaryFor: day1 to: 20. payroll addEmployee: (ralph). self employee: ralph hours: self aLittleOvertime starting: day1. faith := Employee new named: 'Faith Johnson'. faith changeSalaryFor: day1 to: 12. payroll addEmployee: faith. self employee: faith hours: self normalMonth starting: day1. ^payroll! twoWeeksVacation ^#( #(40 0) #(40 0) #(0 40) #(0 40))! ! !PayrollSystem class methodsFor: 'instance creation'! new ^super new initialize! ! Object subclass: #Employee instanceVariableNames: 'name transactions salary earned paid withholding accruedVacation taxRule ' classVariableNames: 'HeadTaxRate MarriedSeparateTaxRate MarriedTaxRate SingleTaxRate ' poolDictionaries: '' category: 'Payroll'! Employee comment: 'I represent an employee in a payroll system. Thus, I keep track of the number of hours worked, the wages paid, the vacation accrued, the taxes withheld, and so on. My values change only when transactions are posted to me. Transactions are subclasses of EmployeeTransaction. Variables name transactions salary earned paid withholding accruedVacation taxRule '! !Employee methodsFor: 'initialize-release'! named: aString name := aString. transactions := OrderedCollection new. salary := 0. earned := 0. paid := 0. withholding := 0. accruedVacation := 0. taxRule := SingleTaxRate! ! !Employee methodsFor: 'accessing'! name ^name! salary ^salary! salary: anAmount salary := anAmount! ! !Employee methodsFor: 'posting'! incrementEarned: anAmount earned := earned + anAmount.! incrementPaid: anAmount paid := paid + anAmount! incrementWithholding: anAmount withholding := withholding + anAmount! postTransaction: aTransaction "Public interface to record and process (i.e. post) a transaction." aTransaction postTo: self. transactions add: aTransaction! ! !Employee methodsFor: 'actions'! changeSalaryFor: aDate to: dollarPerHour self postTransaction: (SalaryChange new date: aDate employee: self salary: dollarPerHour)! makePaycheckAt: aDate "Create a paycheck for the amount owed to the employee. It hasn't been posted." | amountPaid amountWithheld | amountPaid := earned - paid - withholding. amountWithheld := self taxesOn: amountPaid at: aDate. amountPaid := amountPaid - amountWithheld. ^Paycheck new date: aDate employee: self paid: amountPaid withheld: amountWithheld totalPaid: paid + amountPaid totalWithheld: withholding + amountWithheld! postTimeCardFor: aDate hoursWorked: hoursW vacation: hoursV self postTransaction: (Timecard new date: aDate employee: self worked: hoursW vacation: hoursV)! ! !Employee methodsFor: 'private'! federalTaxesOn: money assumingTotalIs: estimatedEarnings ^money * (taxRule rateFor: estimatedEarnings)! taxesOn: money at: date "Return the taxes to be withheld from the money earned." "This is just a guess - I didn't look up the tax rules." | socialSecurity federalTaxes stateTaxes estimatedEarnings | socialSecurity := (0 max: (money min: (60600 - earned))) * 0.077. estimatedEarnings := ((earned) + money) * date day / date daysInYear. federalTaxes := self federalTaxesOn: money assumingTotalIs: estimatedEarnings. stateTaxes := money * 0.03. "Illinois has a flat tax." ^socialSecurity + federalTaxes + stateTaxes! ! "-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "! Employee class instanceVariableNames: ''! !Employee class methodsFor: 'class initialization'! initialize "Employee initialize" SingleTaxRate := TaxRule new. SingleTaxRate breakPoint: 0 rate: 0.15; breakPoint: 22750 rate: 0.28; breakPoint: 55100 rate: 0.31; breakPoint: 115000 rate: 0.36; breakPoint: 250000 rate: 0.396. MarriedTaxRate := TaxRule new. MarriedTaxRate breakPoint: 0 rate: 0.15; breakPoint: 38000 rate: 0.28; breakPoint: 91850 rate: 0.31; breakPoint: 140000 rate: 0.36; breakPoint: 250000 rate: 0.396. MarriedSeparateTaxRate := TaxRule new. MarriedSeparateTaxRate breakPoint: 0 rate: 0.15; breakPoint: 19500 rate: 0.28; breakPoint: 45925 rate: 0.31; breakPoint: 70000 rate: 0.36; breakPoint: 125000 rate: 0.396. HeadTaxRate := TaxRule new. HeadTaxRate breakPoint: 0 rate: 0.15; breakPoint: 30500 rate: 0.28; breakPoint: 78700 rate: 0.31; breakPoint: 127500 rate: 0.36; breakPoint: 250000 rate: 0.396.! ! EmployeeTransaction subclass: #Paycheck instanceVariableNames: 'amountPaid taxes totalPaid totalTaxes ' classVariableNames: 'AmountFormat DateFormat ' poolDictionaries: '' category: 'Payroll'! Paycheck comment: 'Transaction representing a payment to an employee.'! !Paycheck methodsFor: 'initialize-release'! date: aDate employee: anEmployee paid: amount1 withheld: amount2 totalPaid: amount3 totalWithheld: amount4 date := aDate. employee := anEmployee. amountPaid := amount1. taxes := amount2. totalPaid := amount3. totalTaxes := amount4! ! !Paycheck methodsFor: 'accessing'! amountPaid ^amountPaid! taxes ^taxes! ! !Paycheck methodsFor: 'posting'! postTo: anEmployee anEmployee incrementPaid: amountPaid. anEmployee incrementWithholding: taxes! ! !Paycheck methodsFor: 'printing'! printOnCheckStream: aStream "This method is supposed to print information on a check that is on the printer, and aStream is the printer. Unfortunately, the whole method is bogus, because we don't know what the printer or the check are like. I'm assuming that the check has two-parts. One is the actual check, and we need to print the employee's name and the amount. The second is the check stub, which will say how much is being paid and how much is being withheld. I'm also assuming that the check is preprinted with the name of the company, and everything else that is usual for a check." aStream cr; cr. aStream next: 40 put: (Character space). DateFormat print: date on: aStream. aStream cr. aStream nextPutAll: ((String new: 40 withAll: Character space) copyReplaceFrom: 1 to: employee name size with: employee name). AmountFormat print: amountPaid on: aStream. aStream cr; cr; cr; cr. aStream nextPutAll: 'this month '. AmountFormat print: amountPaid on: aStream. AmountFormat print: taxes on: aStream. aStream cr. aStream nextPutAll: 'year to date '. AmountFormat print: totalPaid on: aStream. AmountFormat print: totalTaxes on: aStream. aStream cr; cr.! ! "-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "! Paycheck class instanceVariableNames: ''! !Paycheck class methodsFor: 'class initialization'! initialize "Paycheck initialize" AmountFormat := NumberPrintPolicy newFor: #usCurrency. DateFormat := TimestampPrintPolicy newFor: #us! ! EmployeeTransaction subclass: #Timecard instanceVariableNames: 'hoursWorked hoursVacation ' classVariableNames: '' poolDictionaries: '' category: 'Payroll'! Timecard comment: 'Transaction representing the work that the employee has done. '! !Timecard methodsFor: 'initialize-release'! date: aDate employee: anEmployee worked: hoursW vacation: hoursV date := aDate. employee := anEmployee. hoursWorked := hoursW. hoursVacation := hoursV! ! !Timecard methodsFor: 'accessing'! hoursVacation ^hoursVacation! hoursWorked ^hoursWorked! ! !Timecard methodsFor: 'posting'! postTo: anEmployee | money overtime nonovertime | overtime := (hoursWorked - 40) max: 0. nonovertime := hoursWorked min: 40. money := (overtime * 1.5 + nonovertime) * anEmployee salary. anEmployee incrementEarned: money.! ! EmployeeTransaction subclass: #SalaryChange instanceVariableNames: 'newSalary ' classVariableNames: '' poolDictionaries: '' category: 'Payroll'! SalaryChange comment: 'Transaction representing a change in salary, usually a raise.'! !SalaryChange methodsFor: 'initialize-release'! date: aDate employee: anEmployee salary: dollarPerHour date := aDate. employee := anEmployee. newSalary := dollarPerHour! ! !SalaryChange methodsFor: 'accessing'! newSalary ^newSalary! ! !SalaryChange methodsFor: 'posting'! postTo: anEmployee anEmployee salary: newSalary! ! Employee initialize! Paycheck initialize!