ecom_core 1.2.17 → 1.2.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d4ad578f6c6d7aece965edd8d3ad059afb16ff3f90445ba63f55c6afdc530b8f
4
- data.tar.gz: a31af1754f7ccdc5669a1fe5747745bb8d98d0e82371db261bee62cd08d50f38
3
+ metadata.gz: 226d69727b3ecb4a8edfb5fc4c4a92fb6c91ff999d928f5509acaa9da26ec19a
4
+ data.tar.gz: bb35268fa5cda39ace0d06240ad53f06da6845076df4e23041b6bd3ce147a36b
5
5
  SHA512:
6
- metadata.gz: 7720605e3fa55dd2ff8d12358a5ece0c11f11c47647b4ac1b9be6ca35d308592d3e2ffef33c9beea298c149f5abb301f0612a194934539ab357c78699529d3cb
7
- data.tar.gz: 4c1a5cf5b05c9a8f247fb0b457b9904b9e98bb884a0b5e8a8da38b8268ae7177f631c51ab905dc947042f7117b1f8bf1525a1b92b76d3b1423c24ee157f0af5f
6
+ metadata.gz: 6069a79e8aa94eae4e49e9a68d0783a366fdb92c355d3d73ed4fdbd0ab2feaeb3f2c4c3678d8fda11a0cf7d25b783a89677d213752b1c9e5909e84f3491e52c6
7
+ data.tar.gz: cf82927e16d1809c43760e3238202a87df93c10ae89b06cc482e598758c933d4368ecab45b8fb1d38c4dfec10180ae35e87e8ad785bde60d6329eac9e4c81c2f
@@ -61,7 +61,7 @@ module Ecom
61
61
 
62
62
  raise 'There is no open attendance sheet to submit.' if sheet.nil?
63
63
 
64
- sheet.closed_at = Time.now
64
+ sheet.submitted_at = DateTime.now
65
65
  sheet.status = StatusConstants::SUBMITTED
66
66
  sheet.save
67
67
  sheet
@@ -75,11 +75,27 @@ module Ecom
75
75
  sheet = AttendanceSheet.open_for_date(date, project_id)
76
76
  raise 'There is no open attendance sheet to submit for the selected day.' unless sheet
77
77
 
78
- sheet.closed_at = Time.now
78
+ sheet.submitted_at = DateTime.now
79
79
  sheet.status = StatusConstants::SUBMITTED
80
80
  sheet.save
81
81
  sheet
82
82
  end
83
+
84
+ def submit
85
+ raise 'Attendance sheet is not open and cannot be submitted' if status != StatusConstants::OPEN
86
+
87
+ self.submitted_at = DateTime.now
88
+ self.status = StatusConstants::SUBMITTED
89
+ save
90
+ end
91
+
92
+ def approve
93
+ raise 'Attendance sheet is not submitted and cannot be approved' unless status == StatusConstants::SUBMITTED
94
+
95
+ self.status = StatusConstants::APPROVED
96
+ self.approved_at = DateTime.now
97
+ save
98
+ end
83
99
  end
84
100
  end
85
101
  end
@@ -1,10 +1,33 @@
1
1
  module Ecom
2
2
  module Core
3
3
  class Company < ApplicationRecord
4
+ # The settings attribute is a json string containing setting key value
5
+ # pairs. The valid settings are:
6
+ # payroll_periods => a list of payroll period values. Each
7
+ # payroll period has the following entries:
8
+ # order: A numeric value indicating the order of the period.
9
+ # date: A numeric value indicating the date of each month which
10
+ # upto which timesheet data is used for payroll calculation
11
+ # for the specific period.
12
+ # comparison: One of [:exact, :days_before_month_end]. :exact will
13
+ # force calculations to be made upto the exact mentioned date
14
+ # value, whereas :days_before_month_end is often used for last
15
+ # period payroll calculation and we want to specify on which date
16
+ # before the end of the month we have to limit timesheet data to
17
+ # calculate payroll. E.g. 3 days before month end would be 27th
18
+ # for september and 28th for october.
19
+ #
20
+ # period_count - the total number of periods per month for payroll.
21
+ # this value should align with the defined payroll periods.
22
+ #
4
23
  has_many :projects
5
24
 
6
25
  validates :code, :name, :address, :telephone, presence: true
7
26
  validates :code, :name, uniqueness: true
27
+
28
+ def valid_settings?
29
+ settings['payroll_periods'].count == settings['period_count']
30
+ end
8
31
  end
9
32
  end
10
33
  end
@@ -2,15 +2,15 @@ module Ecom
2
2
  module Core
3
3
  class CrewTime < ApplicationRecord
4
4
  # Time Ranges
5
- MORNING = 'Morning'.freeze
6
- AFTERNOON = 'Afternoon'.freeze
7
- BOTH = 'Both'.freeze
5
+ MORNING = :morning
6
+ AFTERNOON = :afternoon
7
+ FULL_DAY = :full_day
8
8
 
9
- TIME_RANGE = {
10
- MORNING => { start: Time.zone.parse('5:00 AM'), end: Time.zone.parse('9:00 AM') },
11
- AFTERNOON => { start: Time.zone.parse('10:00 AM'), end: Time.zone.parse('2:00 PM') },
12
- BOTH => { start: Time.zone.parse('5:00 AM'), end: Time.zone.parse('2:00 PM') }
13
- }.freeze
9
+ # TIME_RANGE = {
10
+ # MORNING => { start: Time.zone.parse('5:00 AM'), end: Time.zone.parse('9:00 AM') },
11
+ # AFTERNOON => { start: Time.zone.parse('10:00 AM'), end: Time.zone.parse('2:00 PM') },
12
+ # BOTH => { start: Time.zone.parse('5:00 AM'), end: Time.zone.parse('2:00 PM') }
13
+ # }.freeze
14
14
 
15
15
  belongs_to :attendance_sheet_entry
16
16
  belongs_to :revision_to, class_name: 'Ecom::Core::CrewTime', optional: true
@@ -47,15 +47,42 @@ module Ecom
47
47
  attendance_sheet_entry.save
48
48
  end
49
49
 
50
+ # A method to get the available time ranges at a given point.
51
+ # We cannot define the range variables as a constant because
52
+ # the parsing should be done at the exact moment we are about
53
+ # to do time range calculations to avoid errors caused by date
54
+ # mismatches
55
+ def define_range
56
+ morning = {
57
+ start: Time.zone.parse('5:00 AM'),
58
+ finish: Time.zone.parse('9:00 AM')
59
+ }
60
+ afternoon = {
61
+ start: Time.zone.parse('10:00 AM'),
62
+ finish: Time.zone.parse('2:00 PM')
63
+ }
64
+ full_day = {
65
+ start: Time.zone.parse('5:00 AM'),
66
+ finish: Time.zone.parse('2:00 PM')
67
+ }
68
+
69
+ {
70
+ morning: morning,
71
+ afternoon: afternoon,
72
+ full_day: full_day
73
+ }
74
+ end
75
+
50
76
  # A method to check if checkin and checkout range falls in the morning,
51
77
  # afternoon, or both.
52
78
  def find_range(start, finish)
53
- if start.before?(TIME_RANGE[MORNING][:end]) && finish.before?(TIME_RANGE[AFTERNOON][:start])
54
- MORNING
55
- elsif start.after?(TIME_RANGE[MORNING][:end]) && finish.after?(TIME_RANGE[AFTERNOON][:start])
56
- AFTERNOON
79
+ range = define_range
80
+ if start.before?(range[:morning][:finish]) && finish.before?(range[:afternoon][:start])
81
+ :morning
82
+ elsif start.after?(range[:morning][:finish]) && finish.after?(range[:afternoon][:start])
83
+ :afternoon
57
84
  else
58
- BOTH
85
+ :full_day
59
86
  end
60
87
  end
61
88
 
@@ -63,13 +90,14 @@ module Ecom
63
90
  # of the defined morning and afternoon ranges
64
91
  def compute_hours
65
92
  # Reparse time to avoid errors caused by date differences
93
+ range = define_range
66
94
  start = Time.zone.parse(checkin_time.strftime('%I:%M%p'))
67
95
  finish = Time.zone.parse(checkout_time.strftime('%I:%M%p'))
68
- range = find_range(start, finish)
69
- left = start.before?(TIME_RANGE[range][:start]) ? TIME_RANGE[range][:start] : start
70
- right = finish.after?(TIME_RANGE[range][:end]) ? TIME_RANGE[range][:end] : finish
96
+ day_part = find_range(start, finish)
97
+ left = start.before?(range[day_part][:start]) ? range[day_part][:start] : start
98
+ right = finish.after?(range[day_part][:finish]) ? range[day_part][:finish] : finish
71
99
  time = (right - left) / 1.hour
72
- time -= 1 if range == BOTH
100
+ time -= 1 if day_part == FULL_DAY
73
101
  time
74
102
  end
75
103
  end
@@ -24,14 +24,18 @@ module Ecom
24
24
  OvertimeSheet.open(project_id).where(date: date).exists?
25
25
  end
26
26
 
27
+ def self.exists_for_date?(date, project_id)
28
+ OvertimeSheet.by_project(project_id).by_date(date).exists?
29
+ end
30
+
27
31
  # Overtime sheet should be created using the
28
32
  # method below only. This is because we need to
29
33
  # check if there is an open overtime already,
30
34
  # and also that we have only one open overtime
31
35
  # sheet at a time.
32
36
  def self.create_new(date, project_id)
33
- if OvertimeSheet.open_for_date_exists?(date, project_id)
34
- raise 'There is already an open overtime sheet for the selected date.'
37
+ if OvertimeSheet.exists_for_date?(date, project_id)
38
+ raise 'There is already an overtime sheet for the selected date.'
35
39
  end
36
40
 
37
41
  if OvertimeSheet.open_exists?(project_id)
@@ -40,6 +44,22 @@ module Ecom
40
44
 
41
45
  OvertimeSheet.create(date: date, opened_at: Time.now, status: StatusConstants::OPEN, project_id: project_id)
42
46
  end
47
+
48
+ def submit
49
+ raise 'Overtime sheet is not open and cannot be submitted' if status != StatusConstants::OPEN
50
+
51
+ self.submitted_at = DateTime.now
52
+ self.status = StatusConstants::SUBMITTED
53
+ save
54
+ end
55
+
56
+ def approve
57
+ raise 'Overtime sheet is not submitted and cannot be approved' unless status == StatusConstants::SUBMITTED
58
+
59
+ self.status = StatusConstants::APPROVED
60
+ self.approved_at = DateTime.now
61
+ save
62
+ end
43
63
  end
44
64
  end
45
65
  end
@@ -7,6 +7,8 @@ module Ecom
7
7
 
8
8
  before_validation :set_payment_order
9
9
 
10
+ scope :by_status, ->(status) { where(approved: status) }
11
+
10
12
  def set_payment_order
11
13
  unless payroll
12
14
  self.payment_order = 1
@@ -9,6 +9,16 @@ module Ecom
9
9
  scope :by_project, ->(id) { where(project_id: id) }
10
10
  scope :by_month, ->(month) { where(month: month) }
11
11
  scope :by_year, ->(year) { where(year: year) }
12
+
13
+ def create_next
14
+ m = month + 1
15
+ y = year
16
+ if m > 12
17
+ m = 1
18
+ y += 1
19
+ end
20
+ Payroll.create(month: m, year: y)
21
+ end
12
22
  end
13
23
  end
14
24
  end
@@ -6,7 +6,7 @@ class CreateEcomCoreCompanies < ActiveRecord::Migration[6.0]
6
6
  t.string :address, null: false
7
7
  t.string :telephone, null: false
8
8
  t.string :email
9
- t.json :settings
9
+ t.jsonb :settings, default: {}
10
10
 
11
11
  t.timestamps
12
12
  end
@@ -2,8 +2,9 @@ class CreateEcomCoreAttendanceSheets < ActiveRecord::Migration[6.0]
2
2
  def change
3
3
  create_table :ecom_core_attendance_sheets do |t|
4
4
  t.date :date, null: false
5
- t.time :opened_at, null: false
6
- t.time :closed_at
5
+ t.datetime :opened_at, null: false
6
+ t.datetime :submitted_at
7
+ t.datetime :approved_at
7
8
  t.string :remark
8
9
  t.string :status, null: false, default: 'Open'
9
10
  t.references :project,
@@ -2,8 +2,9 @@ class CreateEcomCoreOvertimeSheets < ActiveRecord::Migration[6.0]
2
2
  def change
3
3
  create_table :ecom_core_overtime_sheets do |t|
4
4
  t.date :date, null: false
5
- t.time :opened_at, null: false
6
- t.time :submitted_at
5
+ t.datetime :opened_at, null: false
6
+ t.datetime :submitted_at
7
+ t.datetime :approved_at
7
8
  t.string :remark
8
9
  t.string :status, null: false, default: 'Open'
9
10
  t.references :project,
@@ -1,5 +1,5 @@
1
1
  module Ecom
2
2
  module Core
3
- VERSION = '1.2.17'.freeze
3
+ VERSION = '1.2.22'.freeze
4
4
  end
5
5
  end
@@ -3,6 +3,8 @@ FactoryBot.define do
3
3
  sequence :code do |n|
4
4
  "AMCode#{n}"
5
5
  end
6
- name { FFaker::Name.name }
6
+ sequence :name do |n|
7
+ "AMName#{n}"
8
+ end
7
9
  end
8
10
  end
@@ -1,8 +1,9 @@
1
1
  FactoryBot.define do
2
2
  factory :attendance_sheet, class: Ecom::Core::AttendanceSheet do
3
3
  date { Date.today }
4
- opened_at { Time.now }
5
- closed_at { nil }
4
+ opened_at { DateTime.now }
5
+ submitted_at { nil }
6
+ approved_at { nil }
6
7
  remark { FFaker::Name.name }
7
8
  status { Ecom::Core::StatusConstants::OPEN }
8
9
  association :project
@@ -3,9 +3,17 @@ FactoryBot.define do
3
3
  sequence :code do |n|
4
4
  "CCode#{n}"
5
5
  end
6
- name { FFaker::Name.name }
6
+ sequence :name do |n|
7
+ "CName#{n}"
8
+ end
7
9
  address { FFaker::Address.street_address }
8
10
  telephone { FFaker::PhoneNumber.phone_number }
9
11
  email { FFaker::Internet.email }
12
+ settings do
13
+ {
14
+ payroll_periods: [{ order: 1, date: 25, comparison: :exact }],
15
+ period_count: 1
16
+ }
17
+ end
10
18
  end
11
19
  end
@@ -1,6 +1,10 @@
1
1
  FactoryBot.define do
2
2
  factory :currency, class: Ecom::Core::Currency do
3
- code { FFaker::Currency.code }
4
- name { FFaker::Currency.name }
3
+ sequence :code do |n|
4
+ "CCode#{n}"
5
+ end
6
+ sequence :name do |n|
7
+ "CName#{n}"
8
+ end
5
9
  end
6
10
  end
@@ -1,6 +1,8 @@
1
1
  FactoryBot.define do
2
2
  factory :equipment, class: Ecom::Core::Equipment do
3
- name { FFaker::Name.name }
3
+ sequence :name do |n|
4
+ "EQName#{n}"
5
+ end
4
6
  description { FFaker::Name.name }
5
7
  minimum_acquisition_time { 10 }
6
8
  brands { ['Brand I', 'Brand II', 'Brand III'] }
@@ -1,6 +1,8 @@
1
1
  FactoryBot.define do
2
2
  factory :equipment_category, class: Ecom::Core::EquipmentCategory do
3
- name { FFaker::Name.name }
3
+ sequence :name do |n|
4
+ "ECName#{n}"
5
+ end
4
6
  description { FFaker::Name.name }
5
7
  association :equipment_type
6
8
  end
@@ -1,6 +1,8 @@
1
1
  FactoryBot.define do
2
2
  factory :equipment_location, class: Ecom::Core::EquipmentLocation do
3
- name { FFaker::Name.name }
3
+ sequence :name do |n|
4
+ "ELName#{n}"
5
+ end
4
6
  description { FFaker::Name.name }
5
7
  address { FFaker::Name.name }
6
8
  association :location_type
@@ -1,6 +1,8 @@
1
1
  FactoryBot.define do
2
2
  factory :lookup, class: Ecom::Core::Lookup do
3
- name { FFaker::Name.name }
3
+ sequence :name do |n|
4
+ "LName#{n}"
5
+ end
4
6
  type { 'Ecom::Core::Lookup' }
5
7
  end
6
8
  end
@@ -1,8 +1,9 @@
1
1
  FactoryBot.define do
2
2
  factory :overtime_sheet, class: Ecom::Core::OvertimeSheet do
3
3
  date { Date.today }
4
- opened_at { Time.now }
5
- submitted_at { Time.now + 1.day }
4
+ opened_at { DateTime.now }
5
+ submitted_at { nil }
6
+ approved_at { nil }
6
7
  status { Ecom::Core::StatusConstants::OPEN }
7
8
  association :project
8
9
  end
@@ -1,6 +1,8 @@
1
1
  FactoryBot.define do
2
2
  factory :overtime_type, class: Ecom::Core::OvertimeType do
3
- name { FFaker::Name.name }
3
+ sequence :name do |n|
4
+ "OTName#{n}"
5
+ end
4
6
  from { '07:00 AM' }
5
7
  to { '08:00 AM' }
6
8
  rate { 1.5 }
@@ -1,6 +1,8 @@
1
1
  FactoryBot.define do
2
2
  factory :product_group, class: Ecom::Core::ProductGroup do
3
- name { FFaker::Name.name }
3
+ sequence :name do |n|
4
+ "PGName#{n}"
5
+ end
4
6
  association :project
5
7
  end
6
8
  end
@@ -1,6 +1,8 @@
1
1
  FactoryBot.define do
2
2
  factory :product_type, class: Ecom::Core::ProductType do
3
- name { FFaker::Name.name }
3
+ sequence :name do |n|
4
+ "PTname#{n}"
5
+ end
4
6
  association :work_product_template
5
7
  dimension { [{ name: 'length', label: 'length' }, { name: 'width', label: 'width' }] }
6
8
  end
@@ -3,7 +3,9 @@ FactoryBot.define do
3
3
  sequence :code do |n|
4
4
  "RTCode#{n}"
5
5
  end
6
- name { FFaker::Name.name }
6
+ sequence :name do |n|
7
+ "RTName#{n}"
8
+ end
7
9
  base_unit { FFaker::Name.name }
8
10
  type { 'Ecom::Core::CrewType' }
9
11
  end
@@ -1,5 +1,7 @@
1
1
  FactoryBot.define do
2
2
  factory :stakeholder_type, class: Ecom::Core::StakeholderType do
3
- name { FFaker::Name.name }
3
+ sequence :name do |n|
4
+ "STName#{n}"
5
+ end
4
6
  end
5
7
  end
@@ -1,6 +1,8 @@
1
1
  FactoryBot.define do
2
2
  factory :stakeholder, class: Ecom::Core::Stakeholder do
3
- name { FFaker::Name.unique.name }
3
+ sequence :name do |n|
4
+ "Stakeholder #{n}"
5
+ end
4
6
  type_of_business { FFaker::Name.name }
5
7
  address { FFaker::Name.name }
6
8
  license_no { FFaker::Name.name }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ecom_core
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.17
4
+ version: 1.2.22
5
5
  platform: ruby
6
6
  authors:
7
7
  - Henock L.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-07-20 00:00:00.000000000 Z
11
+ date: 2020-08-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: aasm