moco-ruby 1.0.0 → 1.2.0
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 +4 -4
- data/CHANGELOG.md +36 -1
- data/Gemfile.lock +47 -43
- data/README.md +63 -24
- data/copy_project.rb +337 -0
- data/lib/moco/client.rb +58 -0
- data/lib/moco/connection.rb +45 -22
- data/lib/moco/entities/activity.rb +31 -1
- data/lib/moco/entities/catalog_service.rb +54 -0
- data/lib/moco/entities/comment.rb +61 -0
- data/lib/moco/entities/company.rb +57 -2
- data/lib/moco/entities/contact.rb +56 -0
- data/lib/moco/entities/custom_property.rb +49 -0
- data/lib/moco/entities/deal.rb +38 -2
- data/lib/moco/entities/deal_category.rb +27 -0
- data/lib/moco/entities/employment.rb +55 -0
- data/lib/moco/entities/expense.rb +37 -2
- data/lib/moco/entities/expense_template.rb +39 -0
- data/lib/moco/entities/fixed_cost.rb +30 -0
- data/lib/moco/entities/holiday.rb +33 -2
- data/lib/moco/entities/hourly_rate.rb +33 -0
- data/lib/moco/entities/internal_hourly_rate.rb +32 -0
- data/lib/moco/entities/invoice.rb +81 -1
- data/lib/moco/entities/invoice_bookkeeping_export.rb +33 -0
- data/lib/moco/entities/invoice_payment.rb +51 -0
- data/lib/moco/entities/invoice_reminder.rb +51 -0
- data/lib/moco/entities/offer.rb +122 -0
- data/lib/moco/entities/offer_approval.rb +42 -0
- data/lib/moco/entities/payment_schedule.rb +48 -0
- data/lib/moco/entities/planning_entry.rb +43 -2
- data/lib/moco/entities/presence.rb +34 -2
- data/lib/moco/entities/profile.rb +24 -0
- data/lib/moco/entities/project.rb +76 -2
- data/lib/moco/entities/project_contract.rb +50 -0
- data/lib/moco/entities/project_group.rb +38 -0
- data/lib/moco/entities/purchase.rb +90 -0
- data/lib/moco/entities/purchase_bookkeeping_export.rb +34 -0
- data/lib/moco/entities/purchase_budget.rb +47 -0
- data/lib/moco/entities/purchase_category.rb +38 -0
- data/lib/moco/entities/purchase_draft.rb +25 -0
- data/lib/moco/entities/purchase_payment.rb +51 -0
- data/lib/moco/entities/receipt.rb +55 -0
- data/lib/moco/entities/recurring_expense.rb +55 -0
- data/lib/moco/entities/reports/absences.rb +16 -0
- data/lib/moco/entities/reports/cashflow.rb +16 -0
- data/lib/moco/entities/reports/finance.rb +16 -0
- data/lib/moco/entities/reports/utilization.rb +16 -0
- data/lib/moco/entities/schedule.rb +39 -2
- data/lib/moco/entities/tag.rb +30 -0
- data/lib/moco/entities/tagging.rb +27 -0
- data/lib/moco/entities/task.rb +25 -2
- data/lib/moco/entities/task_template.rb +38 -0
- data/lib/moco/entities/unit.rb +36 -0
- data/lib/moco/entities/user.rb +50 -2
- data/lib/moco/entities/user_role.rb +29 -0
- data/lib/moco/entities/vat_code_purchase.rb +29 -0
- data/lib/moco/entities/vat_code_sale.rb +29 -0
- data/lib/moco/entities/web_hook.rb +32 -2
- data/lib/moco/entities/work_time_adjustment.rb +51 -0
- data/lib/moco/sync.rb +112 -6
- data/lib/moco/version.rb +1 -1
- data/lib/moco-ruby.rb +6 -0
- data/lib/moco.rb +51 -1
- data/moco.gemspec +5 -3
- data/sync_activity.rb +8 -2
- metadata +54 -14
|
@@ -1,8 +1,40 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module MOCO
|
|
4
|
-
# Represents a MOCO presence entry
|
|
5
|
-
#
|
|
4
|
+
# Represents a MOCO presence entry (work time tracking)
|
|
5
|
+
#
|
|
6
|
+
# == Required attributes for create:
|
|
7
|
+
# date - String, "YYYY-MM-DD" date
|
|
8
|
+
# from - String, "HH:MM" start time (e.g., "08:00")
|
|
9
|
+
#
|
|
10
|
+
# == Optional attributes:
|
|
11
|
+
# to - String, "HH:MM" end time (e.g., "17:00"), can be blank for open entry
|
|
12
|
+
# is_home_office - Boolean, whether working from home (default: false)
|
|
13
|
+
#
|
|
14
|
+
# == Read-only attributes:
|
|
15
|
+
# id, user (Hash), created_at, updated_at
|
|
16
|
+
#
|
|
17
|
+
# == Class methods:
|
|
18
|
+
# Presence.touch(client) - Clock in/out (creates or closes presence)
|
|
19
|
+
#
|
|
20
|
+
# == Example:
|
|
21
|
+
# # Log work time
|
|
22
|
+
# moco.presences.create(
|
|
23
|
+
# date: "2024-01-15",
|
|
24
|
+
# from: "09:00",
|
|
25
|
+
# to: "17:30"
|
|
26
|
+
# )
|
|
27
|
+
#
|
|
28
|
+
# # Start work (open-ended)
|
|
29
|
+
# moco.presences.create(
|
|
30
|
+
# date: "2024-01-16",
|
|
31
|
+
# from: "08:30",
|
|
32
|
+
# is_home_office: true
|
|
33
|
+
# )
|
|
34
|
+
#
|
|
35
|
+
# # Clock in/out via touch
|
|
36
|
+
# MOCO::Presence.touch(moco)
|
|
37
|
+
#
|
|
6
38
|
class Presence < BaseEntity
|
|
7
39
|
# Define the specific API path for this entity as a class method
|
|
8
40
|
def self.entity_path
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MOCO
|
|
4
|
+
# Represents the current API user's profile
|
|
5
|
+
# Read-only singleton endpoint for the authenticated user
|
|
6
|
+
#
|
|
7
|
+
# == Read-only attributes:
|
|
8
|
+
# id, firstname, lastname, email, unit (Hash),
|
|
9
|
+
# created_at, updated_at
|
|
10
|
+
#
|
|
11
|
+
# == Usage:
|
|
12
|
+
# profile = moco.profile.get
|
|
13
|
+
# puts "Logged in as: #{profile.firstname} #{profile.lastname}"
|
|
14
|
+
#
|
|
15
|
+
# == Note:
|
|
16
|
+
# This returns information about the user who owns the API key.
|
|
17
|
+
# For other user information, use moco.users.
|
|
18
|
+
#
|
|
19
|
+
class Profile < BaseEntity
|
|
20
|
+
def to_s
|
|
21
|
+
"#{firstname} #{lastname}"
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -1,6 +1,58 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module MOCO
|
|
4
|
+
# Represents a MOCO project
|
|
5
|
+
#
|
|
6
|
+
# == Required attributes for create:
|
|
7
|
+
# name - String, project name (e.g., "Website Relaunch")
|
|
8
|
+
# currency - String, 3-letter currency code (e.g., "EUR", "USD", "CHF")
|
|
9
|
+
# start_date - String, "YYYY-MM-DD" format (required, must be 1st of month if retainer)
|
|
10
|
+
# finish_date - String, "YYYY-MM-DD" format (required, must be last of month if retainer)
|
|
11
|
+
# fixed_price - Boolean, true for fixed-price projects
|
|
12
|
+
# retainer - Boolean, true for retainer/recurring projects
|
|
13
|
+
# leader_id - Integer, user ID of the project leader
|
|
14
|
+
# customer_id - Integer, company ID of the customer
|
|
15
|
+
# identifier - String, project identifier (e.g., "P-123") - only required if manual numbering
|
|
16
|
+
#
|
|
17
|
+
# == Optional attributes:
|
|
18
|
+
# co_leader_id - Integer, user ID of co-leader
|
|
19
|
+
# deal_id - Integer, associated deal ID
|
|
20
|
+
# project_group_id - Integer, project group ID
|
|
21
|
+
# contact_id - Integer, primary contact ID
|
|
22
|
+
# secondary_contact_id - Integer, secondary contact ID
|
|
23
|
+
# billing_contact_id - Integer, billing contact ID
|
|
24
|
+
# billing_address - String, billing address (multiline with \n)
|
|
25
|
+
# billing_email_to - String, email for invoices
|
|
26
|
+
# billing_email_cc - String, CC email for invoices
|
|
27
|
+
# billing_notes - String, notes for billing
|
|
28
|
+
# billing_variant - String, "project", "task", or "user" (default: "project")
|
|
29
|
+
# setting_include_time_report - Boolean, include time report with invoices
|
|
30
|
+
# hourly_rate - Float/Integer, hourly rate (meaning depends on billing_variant)
|
|
31
|
+
# budget - Float/Integer, total budget
|
|
32
|
+
# budget_monthly - Float/Integer, monthly budget (required if retainer: true)
|
|
33
|
+
# budget_expenses - Float/Integer, expenses budget
|
|
34
|
+
# tags - Array of Strings, e.g., ["Print", "Digital"]
|
|
35
|
+
# custom_properties - Hash, e.g., {"PO-Number": "123-ABC"}
|
|
36
|
+
# info - String, additional info
|
|
37
|
+
#
|
|
38
|
+
# == Read-only attributes (returned by API):
|
|
39
|
+
# id, active, color, customer (Hash), leader (Hash), co_leader (Hash),
|
|
40
|
+
# deal (Hash), tasks (Array), contracts (Array), project_group (Hash),
|
|
41
|
+
# created_at, updated_at
|
|
42
|
+
#
|
|
43
|
+
# == Example:
|
|
44
|
+
# moco.projects.create(
|
|
45
|
+
# name: "Website Relaunch",
|
|
46
|
+
# currency: "EUR",
|
|
47
|
+
# start_date: "2024-01-01",
|
|
48
|
+
# finish_date: "2024-12-31",
|
|
49
|
+
# fixed_price: false,
|
|
50
|
+
# retainer: false,
|
|
51
|
+
# leader_id: 123456,
|
|
52
|
+
# customer_id: 789012,
|
|
53
|
+
# budget: 50000
|
|
54
|
+
# )
|
|
55
|
+
#
|
|
4
56
|
class Project < BaseEntity
|
|
5
57
|
def customer
|
|
6
58
|
# Use the association method to fetch the customer
|
|
@@ -31,12 +83,34 @@ module MOCO
|
|
|
31
83
|
end
|
|
32
84
|
|
|
33
85
|
# Fetches tasks associated with this project.
|
|
86
|
+
# Always returns a NestedCollectionProxy for consistent interface.
|
|
87
|
+
# Data is fetched lazily when accessed (e.g., .all, .first, .each).
|
|
88
|
+
# Note: Embedded tasks from projects.assigned are available via attributes[:tasks]
|
|
89
|
+
# but may have incomplete fields compared to the dedicated endpoint.
|
|
34
90
|
def tasks
|
|
35
|
-
# Don't cache the proxy - create a fresh one each time
|
|
36
|
-
# This ensures we get fresh data when tasks are created/updated/deleted
|
|
37
91
|
MOCO::NestedCollectionProxy.new(client, self, :tasks, "Task")
|
|
38
92
|
end
|
|
39
93
|
|
|
94
|
+
# Fetches contracts associated with this project.
|
|
95
|
+
def contracts
|
|
96
|
+
MOCO::NestedCollectionProxy.new(client, self, :contracts, "ProjectContract")
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Fetches payment schedules associated with this project.
|
|
100
|
+
def payment_schedules
|
|
101
|
+
MOCO::NestedCollectionProxy.new(client, self, :payment_schedules, "PaymentSchedule")
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Fetches recurring expenses associated with this project.
|
|
105
|
+
def recurring_expenses
|
|
106
|
+
MOCO::NestedCollectionProxy.new(client, self, :recurring_expenses, "RecurringExpense")
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Get the project group
|
|
110
|
+
def project_group
|
|
111
|
+
association(:project_group)
|
|
112
|
+
end
|
|
113
|
+
|
|
40
114
|
def to_s
|
|
41
115
|
"Project #{identifier} \"#{name}\" (#{id})"
|
|
42
116
|
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MOCO
|
|
4
|
+
# Represents a MOCO project contract/staff assignment
|
|
5
|
+
# (Projekte / Personal) - assigning users to projects
|
|
6
|
+
#
|
|
7
|
+
# == Required attributes for create:
|
|
8
|
+
# user_id - Integer, user to assign to project
|
|
9
|
+
#
|
|
10
|
+
# == Optional attributes:
|
|
11
|
+
# billable - Boolean, whether user's time is billable
|
|
12
|
+
# active - Boolean, whether assignment is active
|
|
13
|
+
# budget - Float, hours budget for this user on project
|
|
14
|
+
# hourly_rate - Float, billing rate for this user on project
|
|
15
|
+
#
|
|
16
|
+
# == Read-only attributes:
|
|
17
|
+
# id, firstname, lastname, created_at, updated_at
|
|
18
|
+
#
|
|
19
|
+
# == Access via project:
|
|
20
|
+
# project = moco.projects.find(123)
|
|
21
|
+
# project.contracts # via nested API
|
|
22
|
+
#
|
|
23
|
+
# == Example:
|
|
24
|
+
# # Assign user to project with budget
|
|
25
|
+
# moco.post("projects/123/contracts", {
|
|
26
|
+
# user_id: 456,
|
|
27
|
+
# budget: 100,
|
|
28
|
+
# hourly_rate: 150.0,
|
|
29
|
+
# billable: true
|
|
30
|
+
# })
|
|
31
|
+
#
|
|
32
|
+
# == Note:
|
|
33
|
+
# Cannot delete assignment if user has tracked hours.
|
|
34
|
+
# user_id cannot be changed after creation.
|
|
35
|
+
#
|
|
36
|
+
class ProjectContract < BaseEntity
|
|
37
|
+
# Associations
|
|
38
|
+
def project
|
|
39
|
+
association(:project)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def user
|
|
43
|
+
association(:user)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def to_s
|
|
47
|
+
"Contract ##{id}"
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MOCO
|
|
4
|
+
# Represents a MOCO project group for organizing projects
|
|
5
|
+
#
|
|
6
|
+
# == Required attributes for create:
|
|
7
|
+
# name - String, group name
|
|
8
|
+
#
|
|
9
|
+
# == Read-only attributes:
|
|
10
|
+
# id, created_at, updated_at
|
|
11
|
+
#
|
|
12
|
+
# == Example:
|
|
13
|
+
# # Create a project group
|
|
14
|
+
# moco.project_groups.create(name: "Website Projects")
|
|
15
|
+
#
|
|
16
|
+
# # Get projects in a group
|
|
17
|
+
# group = moco.project_groups.find(123)
|
|
18
|
+
# group.projects # => Array of Project objects
|
|
19
|
+
#
|
|
20
|
+
# == Note:
|
|
21
|
+
# To assign a project to a group, set project_group_id when
|
|
22
|
+
# creating/updating the project.
|
|
23
|
+
#
|
|
24
|
+
class ProjectGroup < BaseEntity
|
|
25
|
+
def self.entity_path
|
|
26
|
+
"projects/groups"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Get projects in this group
|
|
30
|
+
def projects
|
|
31
|
+
has_many(:projects, :project_group_id)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def to_s
|
|
35
|
+
name.to_s
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MOCO
|
|
4
|
+
# Represents a MOCO purchase/expense (Ausgaben)
|
|
5
|
+
#
|
|
6
|
+
# == Required attributes for create:
|
|
7
|
+
# date - String, "YYYY-MM-DD" format
|
|
8
|
+
# currency - String, valid currency code (e.g., "EUR", "CHF", "USD")
|
|
9
|
+
# payment_method - String, one of:
|
|
10
|
+
# "bank_transfer", "direct_debit", "credit_card",
|
|
11
|
+
# "paypal", "cash", "bank_transfer_swiss_qr_esr"
|
|
12
|
+
# items - Array of item hashes (at least one required):
|
|
13
|
+
# { title: "Item", total: 100.0, tax: 7.7, tax_included: true }
|
|
14
|
+
#
|
|
15
|
+
# == Optional attributes:
|
|
16
|
+
# title - String, purchase title (auto-generated from items if omitted)
|
|
17
|
+
# due_date - String, "YYYY-MM-DD" payment due date
|
|
18
|
+
# service_period_from - String, "YYYY-MM-DD" service period start
|
|
19
|
+
# service_period_to - String, "YYYY-MM-DD" service period end
|
|
20
|
+
# status - String, "pending" (Inbox) or "archived" (Archive)
|
|
21
|
+
# company_id - Integer, supplier company ID
|
|
22
|
+
# user_id - Integer, responsible user ID
|
|
23
|
+
# receipt_identifier - String, supplier's invoice number
|
|
24
|
+
# info - String, free text notes
|
|
25
|
+
# iban - String, bank account for payment
|
|
26
|
+
# reference - String, payment reference
|
|
27
|
+
# custom_property_values - Hash, custom field values
|
|
28
|
+
# tags - Array of Strings, labels
|
|
29
|
+
# file - Hash, { filename: "doc.pdf", base64: "..." }
|
|
30
|
+
#
|
|
31
|
+
# == Item attributes:
|
|
32
|
+
# title - String, item description
|
|
33
|
+
# total - Float, item total amount
|
|
34
|
+
# tax - Float, tax percentage (e.g., 7.7)
|
|
35
|
+
# tax_included - Boolean, whether total includes tax
|
|
36
|
+
# category_id - Integer, purchase category ID
|
|
37
|
+
#
|
|
38
|
+
# == Read-only attributes:
|
|
39
|
+
# id, identifier, net_total, gross_total, payments (Array),
|
|
40
|
+
# approval_status, file_url, company (Hash), user (Hash),
|
|
41
|
+
# created_at, updated_at
|
|
42
|
+
#
|
|
43
|
+
# == Example:
|
|
44
|
+
# moco.purchases.create(
|
|
45
|
+
# date: "2024-01-15",
|
|
46
|
+
# currency: "EUR",
|
|
47
|
+
# payment_method: "bank_transfer",
|
|
48
|
+
# company_id: 456,
|
|
49
|
+
# items: [
|
|
50
|
+
# { title: "Office supplies", total: 119.0, tax: 19.0, tax_included: true }
|
|
51
|
+
# ],
|
|
52
|
+
# tags: ["Office"]
|
|
53
|
+
# )
|
|
54
|
+
#
|
|
55
|
+
class Purchase < BaseEntity
|
|
56
|
+
# Assign purchase to a project expense
|
|
57
|
+
def assign_to_project(project_id:, project_expense_id: nil)
|
|
58
|
+
payload = { project_id: }
|
|
59
|
+
payload[:project_expense_id] = project_expense_id if project_expense_id
|
|
60
|
+
|
|
61
|
+
client.post("purchases/#{id}/assign_to_project", payload)
|
|
62
|
+
reload
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Update purchase status (pending/archived)
|
|
66
|
+
def update_status(status)
|
|
67
|
+
client.patch("purchases/#{id}/update_status", { status: })
|
|
68
|
+
reload
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Store/upload a document for this purchase
|
|
72
|
+
def store_document(file_data)
|
|
73
|
+
client.patch("purchases/#{id}/store_document", file_data)
|
|
74
|
+
self
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Associations
|
|
78
|
+
def company
|
|
79
|
+
association(:company)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def user
|
|
83
|
+
association(:user)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def to_s
|
|
87
|
+
"#{title} (#{date})"
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MOCO
|
|
4
|
+
# Represents a MOCO purchase bookkeeping export
|
|
5
|
+
# (Ausgaben / Buchhaltungsexporte)
|
|
6
|
+
# Exports purchase data for accounting systems
|
|
7
|
+
#
|
|
8
|
+
# == Read-only attributes:
|
|
9
|
+
# id, from, to, file_url, user (Hash),
|
|
10
|
+
# created_at, updated_at
|
|
11
|
+
#
|
|
12
|
+
# == Filtering:
|
|
13
|
+
# moco.purchase_bookkeeping_exports.all
|
|
14
|
+
# moco.purchase_bookkeeping_exports.where(from: "2024-01-01", to: "2024-01-31")
|
|
15
|
+
#
|
|
16
|
+
# == Note:
|
|
17
|
+
# Bookkeeping exports are generated via MOCO's finance interface.
|
|
18
|
+
# This endpoint provides read-only access to export records.
|
|
19
|
+
#
|
|
20
|
+
class PurchaseBookkeepingExport < BaseEntity
|
|
21
|
+
# Custom path since it's nested under purchases
|
|
22
|
+
def self.entity_path
|
|
23
|
+
"purchases/bookkeeping_exports"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def user
|
|
27
|
+
association(:user, "User")
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def to_s
|
|
31
|
+
"PurchaseBookkeepingExport #{id} (#{from} - #{to})"
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MOCO
|
|
4
|
+
# Represents a MOCO purchase budget (Ausgaben – Budgets)
|
|
5
|
+
# Read-only budget tracking for expense categories
|
|
6
|
+
#
|
|
7
|
+
# == Read-only attributes:
|
|
8
|
+
# id, title, year, target, exhausted, remaining,
|
|
9
|
+
# created_at, updated_at
|
|
10
|
+
#
|
|
11
|
+
# == Helper methods:
|
|
12
|
+
# remaining_percentage - Percentage of budget remaining
|
|
13
|
+
# exhausted_percentage - Percentage of budget used
|
|
14
|
+
#
|
|
15
|
+
# == Example:
|
|
16
|
+
# budgets = moco.purchase_budgets.all
|
|
17
|
+
# budgets.each do |budget|
|
|
18
|
+
# puts "#{budget.title}: #{budget.remaining_percentage}% remaining"
|
|
19
|
+
# end
|
|
20
|
+
#
|
|
21
|
+
# == Note:
|
|
22
|
+
# Purchase budgets are configured in MOCO's admin interface.
|
|
23
|
+
# This endpoint provides read-only access for tracking.
|
|
24
|
+
#
|
|
25
|
+
class PurchaseBudget < BaseEntity
|
|
26
|
+
# Custom path since it's nested under purchases
|
|
27
|
+
def self.entity_path
|
|
28
|
+
"purchases/budgets"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def to_s
|
|
32
|
+
"PurchaseBudget #{id}: #{title} (#{year})"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def remaining_percentage
|
|
36
|
+
return 0 if target.to_f.zero?
|
|
37
|
+
|
|
38
|
+
(remaining.to_f / target.to_f * 100).round(1)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def exhausted_percentage
|
|
42
|
+
return 0 if target.to_f.zero?
|
|
43
|
+
|
|
44
|
+
(exhaused.to_f / target.to_f * 100).round(1)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MOCO
|
|
4
|
+
# Represents a MOCO purchase category (Ausgaben – Kategorien)
|
|
5
|
+
# Read-only expense categories for organizing purchases
|
|
6
|
+
#
|
|
7
|
+
# == Read-only attributes:
|
|
8
|
+
# id, name, credit_account, active, created_at, updated_at
|
|
9
|
+
#
|
|
10
|
+
# == Example:
|
|
11
|
+
# # List all categories
|
|
12
|
+
# moco.purchase_categories.all
|
|
13
|
+
#
|
|
14
|
+
# # Use category when creating purchase
|
|
15
|
+
# moco.purchases.create(
|
|
16
|
+
# # ... other fields ...
|
|
17
|
+
# items: [{
|
|
18
|
+
# title: "Travel",
|
|
19
|
+
# total: 250.0,
|
|
20
|
+
# tax: 7.7,
|
|
21
|
+
# category_id: 123 # from purchase_categories
|
|
22
|
+
# }]
|
|
23
|
+
# )
|
|
24
|
+
#
|
|
25
|
+
# == Note:
|
|
26
|
+
# Purchase categories are configured in MOCO's admin interface.
|
|
27
|
+
# This endpoint provides read-only access.
|
|
28
|
+
#
|
|
29
|
+
class PurchaseCategory < BaseEntity
|
|
30
|
+
def self.entity_path
|
|
31
|
+
"purchases/categories"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def to_s
|
|
35
|
+
name.to_s
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MOCO
|
|
4
|
+
# Represents a MOCO purchase draft
|
|
5
|
+
# Auto-created draft purchases from document scanning
|
|
6
|
+
#
|
|
7
|
+
# == Read-only attributes:
|
|
8
|
+
# id, title, date, company (Hash), file_url,
|
|
9
|
+
# items (Array), created_at, updated_at
|
|
10
|
+
#
|
|
11
|
+
# == Note:
|
|
12
|
+
# Purchase drafts are created automatically when documents
|
|
13
|
+
# are uploaded/scanned in MOCO's inbox.
|
|
14
|
+
# Convert to actual purchases via the MOCO interface.
|
|
15
|
+
#
|
|
16
|
+
class PurchaseDraft < BaseEntity
|
|
17
|
+
def self.entity_path
|
|
18
|
+
"purchases/drafts"
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def to_s
|
|
22
|
+
"Draft ##{id}"
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MOCO
|
|
4
|
+
# Represents a MOCO purchase payment record (Ausgaben / Zahlungen)
|
|
5
|
+
# For tracking payments made for purchases
|
|
6
|
+
#
|
|
7
|
+
# == Required attributes for create:
|
|
8
|
+
# date - String, "YYYY-MM-DD" payment date
|
|
9
|
+
# total - Float, payment amount
|
|
10
|
+
#
|
|
11
|
+
# == Optional attributes:
|
|
12
|
+
# purchase_id - Integer, purchase being paid (required unless description set)
|
|
13
|
+
# description - String, payment description (required if no purchase_id)
|
|
14
|
+
#
|
|
15
|
+
# == Read-only attributes:
|
|
16
|
+
# id, purchase (Hash), created_at, updated_at
|
|
17
|
+
#
|
|
18
|
+
# == Example:
|
|
19
|
+
# moco.purchase_payments.create(
|
|
20
|
+
# date: "2024-01-20",
|
|
21
|
+
# purchase_id: 456,
|
|
22
|
+
# total: 1500.0
|
|
23
|
+
# )
|
|
24
|
+
#
|
|
25
|
+
# == Bulk create:
|
|
26
|
+
# moco.post("purchases/payments/bulk", {
|
|
27
|
+
# bulk_data: [
|
|
28
|
+
# { date: "2024-01-20", purchase_id: 123, total: 500 },
|
|
29
|
+
# { date: "2024-01-21", description: "Salaries", total: 10000 }
|
|
30
|
+
# ]
|
|
31
|
+
# })
|
|
32
|
+
#
|
|
33
|
+
# == Filtering:
|
|
34
|
+
# moco.purchase_payments.where(purchase_id: 456)
|
|
35
|
+
# moco.purchase_payments.where(date_from: "2024-01-01", date_to: "2024-01-31")
|
|
36
|
+
#
|
|
37
|
+
class PurchasePayment < BaseEntity
|
|
38
|
+
# Custom path since it's nested under purchases
|
|
39
|
+
def self.entity_path
|
|
40
|
+
"purchases/payments"
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def purchase
|
|
44
|
+
association(:purchase, "Purchase")
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def to_s
|
|
48
|
+
"PurchasePayment #{id}: #{total} on #{date}"
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MOCO
|
|
4
|
+
# Represents a MOCO receipt/expense claim (Spesen)
|
|
5
|
+
#
|
|
6
|
+
# == Required attributes for create:
|
|
7
|
+
# date - String, "YYYY-MM-DD" format
|
|
8
|
+
# title - String, receipt description (e.g., "Team lunch")
|
|
9
|
+
# currency - String, valid currency code (e.g., "EUR", "CHF")
|
|
10
|
+
# items - Array of item hashes (at least one required):
|
|
11
|
+
# { vat_code_id: 186, gross_total: 99.90 }
|
|
12
|
+
#
|
|
13
|
+
# == Optional attributes:
|
|
14
|
+
# project_id - Integer, project to associate with
|
|
15
|
+
# info - String, additional notes
|
|
16
|
+
# billable - Boolean, whether expense is billable to customer
|
|
17
|
+
# attachment - Hash, { filename: "receipt.pdf", base64: "..." }
|
|
18
|
+
#
|
|
19
|
+
# == Item attributes:
|
|
20
|
+
# vat_code_id - Integer, VAT code ID (required)
|
|
21
|
+
# gross_total - Float, total amount including tax (required)
|
|
22
|
+
# purchase_category_id - Integer, expense category
|
|
23
|
+
#
|
|
24
|
+
# == Read-only attributes:
|
|
25
|
+
# id, pending, user (Hash), project (Hash), refund_request (Hash),
|
|
26
|
+
# attachment_url, created_at, updated_at
|
|
27
|
+
#
|
|
28
|
+
# == Example:
|
|
29
|
+
# moco.receipts.create(
|
|
30
|
+
# date: "2024-01-15",
|
|
31
|
+
# title: "Client lunch",
|
|
32
|
+
# currency: "EUR",
|
|
33
|
+
# project_id: 123,
|
|
34
|
+
# billable: true,
|
|
35
|
+
# items: [
|
|
36
|
+
# { vat_code_id: 186, gross_total: 85.50 }
|
|
37
|
+
# ]
|
|
38
|
+
# )
|
|
39
|
+
#
|
|
40
|
+
# == Filtering:
|
|
41
|
+
# moco.receipts.where(from: "2024-01-01", to: "2024-01-31")
|
|
42
|
+
# moco.receipts.where(project_id: 123)
|
|
43
|
+
# moco.receipts.where(user_id: 456)
|
|
44
|
+
#
|
|
45
|
+
class Receipt < BaseEntity
|
|
46
|
+
# Associations
|
|
47
|
+
def user
|
|
48
|
+
association(:user)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def to_s
|
|
52
|
+
"#{title} (#{date})"
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MOCO
|
|
4
|
+
# Represents a MOCO project recurring expense
|
|
5
|
+
# (Wiederkehrende Zusatzleistungen) for ongoing services like hosting
|
|
6
|
+
#
|
|
7
|
+
# == Required attributes for create:
|
|
8
|
+
# start_date - String, "YYYY-MM-DD" when recurring starts
|
|
9
|
+
# period - String, recurrence interval:
|
|
10
|
+
# "weekly", "biweekly", "monthly", "quarterly",
|
|
11
|
+
# "biannual", "annual"
|
|
12
|
+
# title - String, expense name (e.g., "Monthly Hosting")
|
|
13
|
+
# quantity - Float, number of units per period
|
|
14
|
+
# unit - String, unit type (e.g., "Server", "License")
|
|
15
|
+
# unit_price - Float, price per unit
|
|
16
|
+
# unit_cost - Float, internal cost per unit
|
|
17
|
+
#
|
|
18
|
+
# == Optional attributes:
|
|
19
|
+
# finish_date - String, "YYYY-MM-DD" when to stop (null = unlimited)
|
|
20
|
+
# description - String, detailed description
|
|
21
|
+
# billable - Boolean, whether to invoice (default: true)
|
|
22
|
+
# budget_relevant - Boolean, count toward budget (default: false)
|
|
23
|
+
# service_period_direction - String, "none", "forward", "backward"
|
|
24
|
+
# custom_properties - Hash, custom field values
|
|
25
|
+
#
|
|
26
|
+
# == Read-only attributes:
|
|
27
|
+
# id, price, cost, currency, recur_next_date, project (Hash),
|
|
28
|
+
# revenue_category (Hash), created_at, updated_at
|
|
29
|
+
#
|
|
30
|
+
# == Example:
|
|
31
|
+
# moco.post("projects/123/recurring_expenses", {
|
|
32
|
+
# start_date: "2024-01-01",
|
|
33
|
+
# period: "monthly",
|
|
34
|
+
# title: "Web Hosting",
|
|
35
|
+
# quantity: 1,
|
|
36
|
+
# unit: "Server",
|
|
37
|
+
# unit_price: 99.0,
|
|
38
|
+
# unit_cost: 50.0,
|
|
39
|
+
# billable: true
|
|
40
|
+
# })
|
|
41
|
+
#
|
|
42
|
+
# == Note:
|
|
43
|
+
# start_date and period cannot be modified after creation.
|
|
44
|
+
#
|
|
45
|
+
class RecurringExpense < BaseEntity
|
|
46
|
+
# Associations
|
|
47
|
+
def project
|
|
48
|
+
association(:project)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def to_s
|
|
52
|
+
"RecurringExpense ##{id} - #{title}"
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MOCO
|
|
4
|
+
module Reports
|
|
5
|
+
# Represents a MOCO absences report (read-only)
|
|
6
|
+
class Absences < BaseEntity
|
|
7
|
+
def self.entity_path
|
|
8
|
+
"report/absences"
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def to_s
|
|
12
|
+
"AbsencesReport"
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module MOCO
|
|
4
|
+
module Reports
|
|
5
|
+
# Represents a MOCO cashflow report (read-only)
|
|
6
|
+
class Cashflow < BaseEntity
|
|
7
|
+
def self.entity_path
|
|
8
|
+
"report/cashflow"
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def to_s
|
|
12
|
+
"CashflowReport"
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|