xeroizer-float 2.15.3.1
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.
- data/.bundle/config +2 -0
- data/.gitattributes +22 -0
- data/Gemfile +19 -0
- data/Gemfile.lock +57 -0
- data/LICENSE.txt +20 -0
- data/README.md +529 -0
- data/Rakefile +55 -0
- data/VERSION +1 -0
- data/lib/big_decimal_to_s.rb +9 -0
- data/lib/class_level_inheritable_attributes.rb +25 -0
- data/lib/xeroizer.rb +64 -0
- data/lib/xeroizer/application_http_proxy.rb +30 -0
- data/lib/xeroizer/ca-certificates.crt +2642 -0
- data/lib/xeroizer/configuration.rb +19 -0
- data/lib/xeroizer/exceptions.rb +129 -0
- data/lib/xeroizer/generic_application.rb +54 -0
- data/lib/xeroizer/http.rb +167 -0
- data/lib/xeroizer/http_encoding_helper.rb +49 -0
- data/lib/xeroizer/logging.rb +8 -0
- data/lib/xeroizer/models/account.rb +67 -0
- data/lib/xeroizer/models/address.rb +32 -0
- data/lib/xeroizer/models/bank_account.rb +12 -0
- data/lib/xeroizer/models/bank_transaction.rb +75 -0
- data/lib/xeroizer/models/branding_theme.rb +22 -0
- data/lib/xeroizer/models/contact.rb +49 -0
- data/lib/xeroizer/models/contact_group.rb +16 -0
- data/lib/xeroizer/models/credit_note.rb +130 -0
- data/lib/xeroizer/models/currency.rb +18 -0
- data/lib/xeroizer/models/employee.rb +28 -0
- data/lib/xeroizer/models/external_link.rb +16 -0
- data/lib/xeroizer/models/invoice.rb +192 -0
- data/lib/xeroizer/models/item.rb +27 -0
- data/lib/xeroizer/models/item_purchase_details.rb +19 -0
- data/lib/xeroizer/models/item_sales_details.rb +19 -0
- data/lib/xeroizer/models/journal.rb +25 -0
- data/lib/xeroizer/models/journal_line.rb +28 -0
- data/lib/xeroizer/models/journal_line_tracking_category.rb +20 -0
- data/lib/xeroizer/models/line_amount_type.rb +11 -0
- data/lib/xeroizer/models/line_item.rb +40 -0
- data/lib/xeroizer/models/line_item_sum.rb +21 -0
- data/lib/xeroizer/models/manual_journal.rb +42 -0
- data/lib/xeroizer/models/manual_journal_line.rb +27 -0
- data/lib/xeroizer/models/option.rb +18 -0
- data/lib/xeroizer/models/organisation.rb +34 -0
- data/lib/xeroizer/models/payment.rb +42 -0
- data/lib/xeroizer/models/phone.rb +25 -0
- data/lib/xeroizer/models/tax_rate.rb +25 -0
- data/lib/xeroizer/models/tracking_category.rb +23 -0
- data/lib/xeroizer/models/tracking_category_child.rb +20 -0
- data/lib/xeroizer/oauth.rb +130 -0
- data/lib/xeroizer/partner_application.rb +46 -0
- data/lib/xeroizer/private_application.rb +27 -0
- data/lib/xeroizer/public_application.rb +21 -0
- data/lib/xeroizer/record/application_helper.rb +32 -0
- data/lib/xeroizer/record/base.rb +155 -0
- data/lib/xeroizer/record/base_model.rb +144 -0
- data/lib/xeroizer/record/base_model_http_proxy.rb +117 -0
- data/lib/xeroizer/record/model_definition_helper.rb +99 -0
- data/lib/xeroizer/record/record_association_helper.rb +140 -0
- data/lib/xeroizer/record/validation_helper.rb +71 -0
- data/lib/xeroizer/record/validators/associated_validator.rb +33 -0
- data/lib/xeroizer/record/validators/block_validator.rb +22 -0
- data/lib/xeroizer/record/validators/inclusion_of_validator.rb +22 -0
- data/lib/xeroizer/record/validators/presence_of_validator.rb +19 -0
- data/lib/xeroizer/record/validators/validator.rb +33 -0
- data/lib/xeroizer/record/xml_helper.rb +133 -0
- data/lib/xeroizer/report/aged_receivables_by_contact.rb +43 -0
- data/lib/xeroizer/report/base.rb +43 -0
- data/lib/xeroizer/report/cell.rb +32 -0
- data/lib/xeroizer/report/cell_xml_helper.rb +74 -0
- data/lib/xeroizer/report/factory.rb +53 -0
- data/lib/xeroizer/report/row/header.rb +12 -0
- data/lib/xeroizer/report/row/row.rb +48 -0
- data/lib/xeroizer/report/row/section.rb +10 -0
- data/lib/xeroizer/report/row/summary.rb +9 -0
- data/lib/xeroizer/report/row/xml_helper.rb +77 -0
- data/lib/xeroizer/report/xml_helper.rb +58 -0
- data/lib/xeroizer/response.rb +55 -0
- data/test/acceptance/about_creating_bank_transactions_test.rb +162 -0
- data/test/acceptance/about_fetching_bank_transactions_test.rb +56 -0
- data/test/acceptance/acceptance_test.rb +53 -0
- data/test/acceptance/bank_transaction_reference_data.rb +31 -0
- data/test/stub_responses/accounts.xml +589 -0
- data/test/stub_responses/api_exception.xml +153 -0
- data/test/stub_responses/bogus_oauth_error +1 -0
- data/test/stub_responses/branding_themes.xml +20 -0
- data/test/stub_responses/contact.xml +40 -0
- data/test/stub_responses/contacts.xml +0 -0
- data/test/stub_responses/create_credit_note.xml +104 -0
- data/test/stub_responses/create_invoice.xml +64 -0
- data/test/stub_responses/credit_note.xml +75 -0
- data/test/stub_responses/credit_note_not_found_error.xml +1 -0
- data/test/stub_responses/credit_notes.xml +169 -0
- data/test/stub_responses/currencies.xml +12 -0
- data/test/stub_responses/employees.xml +29 -0
- data/test/stub_responses/invalid_api_key_error.xml +1 -0
- data/test/stub_responses/invalid_consumer_key +1 -0
- data/test/stub_responses/invalid_request_token +1 -0
- data/test/stub_responses/invoice.xml +84 -0
- data/test/stub_responses/invoice_not_found_error.xml +1 -0
- data/test/stub_responses/invoices.xml +1899 -0
- data/test/stub_responses/items.xml +112 -0
- data/test/stub_responses/manual_journal.xml +45 -0
- data/test/stub_responses/manual_journals.xml +40 -0
- data/test/stub_responses/organisation.xml +25 -0
- data/test/stub_responses/organisations.xml +25 -0
- data/test/stub_responses/payments.xml +1121 -0
- data/test/stub_responses/rate_limit_exceeded +1 -0
- data/test/stub_responses/records/contact-043892a1-aef1-4c18-88d8-b8ccb6d31466.xml +38 -0
- data/test/stub_responses/records/contact-09664078-efe2-4a88-89a5-67eac9b0047b.xml +40 -0
- data/test/stub_responses/records/contact-0a4cf37b-a1a8-4753-9ee2-f9207f63a8ff.xml +48 -0
- data/test/stub_responses/records/contact-0e74f929-11b9-4255-a035-1fdfe573e676.xml +40 -0
- data/test/stub_responses/records/contact-0f471ca5-15c9-405e-a1b9-7cc35194b673.xml +38 -0
- data/test/stub_responses/records/contact-13cd4c47-baa6-4f07-93f6-6442310df4bf.xml +47 -0
- data/test/stub_responses/records/contact-158a2667-82ee-43bf-8f33-a6cc9524092d.xml +38 -0
- data/test/stub_responses/records/contact-17465072-6fa3-40bf-bc42-97765d9e1bea.xml +38 -0
- data/test/stub_responses/records/contact-1975b0ed-b7ba-4c61-bae8-2aa6d78b0dee.xml +39 -0
- data/test/stub_responses/records/contact-1b2be6e9-8d58-4da9-aaf8-4fe5471b653c.xml +53 -0
- data/test/stub_responses/records/contact-1c40da58-fe1d-4e97-b729-b2abdae94d9e.xml +38 -0
- data/test/stub_responses/records/contact-258176a5-c622-4394-9c94-6f88c3ea12e5.xml +40 -0
- data/test/stub_responses/records/contact-299dd3a0-a417-4a37-8a04-2f55e91963e5.xml +40 -0
- data/test/stub_responses/records/contact-2be39278-5154-4ed1-8eb0-676f25acfc66.xml +40 -0
- data/test/stub_responses/records/contact-2e58cff6-488c-4a32-884b-baf848010229.xml +40 -0
- data/test/stub_responses/records/contact-2faccd41-935e-40aa-b74e-e2fc28ac34c3.xml +38 -0
- data/test/stub_responses/records/contact-31af01e7-2ca7-45b9-a500-b02db996568e.xml +38 -0
- data/test/stub_responses/records/contact-344f1113-a25b-4344-b82e-bedeacc17c8e.xml +40 -0
- data/test/stub_responses/records/contact-3e776c4b-ea9e-4bb1-96be-6b0c7a71a37f.xml +39 -0
- data/test/stub_responses/records/contact-3fc1fc6c-e5ff-4e40-b6f3-7eb535637d87.xml +38 -0
- data/test/stub_responses/records/contact-416ab20c-5357-4beb-a740-e8d175d71efb.xml +38 -0
- data/test/stub_responses/records/contact-41a42865-f15a-4fa1-b643-47877608f557.xml +54 -0
- data/test/stub_responses/records/contact-42771b60-19a7-4692-af81-dd9f9b9362d4.xml +43 -0
- data/test/stub_responses/records/contact-451ceb28-9610-44c9-8f35-3225482f2413.xml +40 -0
- data/test/stub_responses/records/contact-4ab343ad-1ebb-4afe-9d48-1814a93c2081.xml +38 -0
- data/test/stub_responses/records/contact-4bb77692-42d4-4565-85a0-8849eb85e039.xml +39 -0
- data/test/stub_responses/records/contact-4dec292f-3ab7-46a8-83e4-5fb5eac42c7f.xml +40 -0
- data/test/stub_responses/records/contact-4e2f192e-8397-4d4d-97ca-a4fc5ac531bf.xml +38 -0
- data/test/stub_responses/records/contact-5188c17c-7786-4436-ad6e-9da2997386d0.xml +40 -0
- data/test/stub_responses/records/contact-52442753-b1c4-40b7-9b79-c33997de5837.xml +40 -0
- data/test/stub_responses/records/contact-565acaa9-e7f3-4fbf-80c3-16b081ddae10.xml +38 -0
- data/test/stub_responses/records/contact-571a2414-81ff-4f8f-8498-d91d83793131.xml +44 -0
- data/test/stub_responses/records/contact-58697449-85ef-46ae-83fc-6a9446f037fb.xml +40 -0
- data/test/stub_responses/records/contact-58bf2ae3-5144-4628-8de2-e165ac2bcdc6.xml +40 -0
- data/test/stub_responses/records/contact-5d41dafd-eb7e-42c1-bd5a-ba3be1da0960.xml +38 -0
- data/test/stub_responses/records/contact-5f005a09-5ce4-4fb4-8096-e69c18be636e.xml +38 -0
- data/test/stub_responses/records/contact-60d578d9-3e10-4aef-b5dc-9d9fd60a3633.xml +47 -0
- data/test/stub_responses/records/contact-62392126-dba4-4a75-b907-5875ebf75259.xml +40 -0
- data/test/stub_responses/records/contact-642c7fb5-e8e5-48e1-a710-39a18c6c3217.xml +40 -0
- data/test/stub_responses/records/contact-64aebf9c-bb89-4b38-b99b-405bd1ece6fd.xml +40 -0
- data/test/stub_responses/records/contact-64eedbc9-1fa0-485a-837f-705f23188161.xml +38 -0
- data/test/stub_responses/records/contact-65e96c9f-1595-4653-9a8a-2a36d49223c2.xml +40 -0
- data/test/stub_responses/records/contact-67d26b93-ccb4-4890-9bf1-284b70ea755d.xml +38 -0
- data/test/stub_responses/records/contact-69d3e538-44b3-4e00-a5f6-7dddcb6e0656.xml +40 -0
- data/test/stub_responses/records/contact-6a8450bc-f81a-4bb0-a8f6-aa4afe9497c7.xml +40 -0
- data/test/stub_responses/records/contact-6c70e424-41d6-4b9b-af3e-b3a9f3589106.xml +40 -0
- data/test/stub_responses/records/contact-6de0b0cf-560c-4503-aab3-e1543c329deb.xml +54 -0
- data/test/stub_responses/records/contact-72dd6a02-396e-42a2-a4d6-cc3fa75dfece.xml +41 -0
- data/test/stub_responses/records/contact-755f1475-d255-43a8-bedc-5ea7fd26c71f.xml +51 -0
- data/test/stub_responses/records/contact-78a9d0a0-3d8c-4f84-af3e-f260bf4a9dc0.xml +38 -0
- data/test/stub_responses/records/contact-79aa39ca-22b0-42c2-9026-78757a29d665.xml +42 -0
- data/test/stub_responses/records/contact-804f4140-5978-48fe-ba20-b56e5b834b18.xml +40 -0
- data/test/stub_responses/records/contact-812d4f28-1681-4241-8e34-d15c5520ba35.xml +38 -0
- data/test/stub_responses/records/contact-860b99a9-0958-4c8d-a98f-bb1f092b16bb.xml +60 -0
- data/test/stub_responses/records/contact-87c8da45-97cc-46be-b170-398da0eacfb8.xml +40 -0
- data/test/stub_responses/records/contact-8a154a19-6c6c-404b-bbc9-6deae2d18251.xml +38 -0
- data/test/stub_responses/records/contact-8bb6931d-2865-44e9-9a23-ed1fb9c7a46c.xml +40 -0
- data/test/stub_responses/records/contact-936c9759-01da-4063-b472-424ab9f48212.xml +38 -0
- data/test/stub_responses/records/contact-9d12a994-9640-4b75-95cc-3de1e9d0ef09.xml +38 -0
- data/test/stub_responses/records/contact-9fe59245-1fbb-4157-93c3-dc97388f3746.xml +40 -0
- data/test/stub_responses/records/contact-a06a7225-6f8a-4522-8400-c534dd43a16e.xml +40 -0
- data/test/stub_responses/records/contact-a76a85fe-73a2-46fa-aba7-791f36103cdb.xml +40 -0
- data/test/stub_responses/records/contact-a93b5f40-0346-4d21-9181-431e129911c0.xml +40 -0
- data/test/stub_responses/records/contact-abf272dd-6b1d-4829-af88-c57bf55855e3.xml +38 -0
- data/test/stub_responses/records/contact-ad24c33b-256b-4157-ad56-cbcf0e8db7b1.xml +47 -0
- data/test/stub_responses/records/contact-b107129d-f4c9-438e-9573-64b778527f4a.xml +40 -0
- data/test/stub_responses/records/contact-b233288a-aa26-4b26-9fc7-779d797dd56f.xml +40 -0
- data/test/stub_responses/records/contact-b2b5333a-2546-4975-891f-d71a8a640d23.xml +38 -0
- data/test/stub_responses/records/contact-b4d149bf-1823-4bd2-96da-9032388c9686.xml +40 -0
- data/test/stub_responses/records/contact-b78d4fd1-4306-4d83-a0b9-61458d1c53a2.xml +40 -0
- data/test/stub_responses/records/contact-b7d108a8-d5f7-4f16-a7c9-26eaed98e8de.xml +40 -0
- data/test/stub_responses/records/contact-baeed0f3-7989-4874-99b3-59f23032cb73.xml +38 -0
- data/test/stub_responses/records/contact-bc51a3a1-b7f6-46ca-ac9e-19b87e6ca100.xml +40 -0
- data/test/stub_responses/records/contact-be9f3aab-52f5-4d9c-94b4-87f7d9e5ee8b.xml +38 -0
- data/test/stub_responses/records/contact-c135f994-01e4-427b-9e15-acfe8a477c16.xml +49 -0
- data/test/stub_responses/records/contact-c14edf75-15e4-4a9c-86e4-f52e2fe7cfa4.xml +40 -0
- data/test/stub_responses/records/contact-ca9b9abc-c2dc-4221-8101-31f464d314cc.xml +44 -0
- data/test/stub_responses/records/contact-cc4db604-9ed8-4eef-8a29-51b5b70496a0.xml +38 -0
- data/test/stub_responses/records/contact-cce9b044-be4a-43b3-9dc7-c027d8dd35b2.xml +38 -0
- data/test/stub_responses/records/contact-d0cd2c4f-18a0-4f7c-a32a-2db00f29d298.xml +43 -0
- data/test/stub_responses/records/contact-d6851dc2-9ed9-4515-bc0b-810b09c06a6a.xml +38 -0
- data/test/stub_responses/records/contact-d6a384fb-f46f-41a3-8ac7-b7bc9e0b5efa.xml +46 -0
- data/test/stub_responses/records/contact-d74e61cf-2ad0-4f0d-b9d1-6a808e3f70cf.xml +40 -0
- data/test/stub_responses/records/contact-d9ab0f61-3b56-4e2b-be39-f33c11bd99e3.xml +40 -0
- data/test/stub_responses/records/contact-dbb1f0b5-a71b-4458-8462-104acd0fec6b.xml +38 -0
- data/test/stub_responses/records/contact-dd981bd6-40dd-496d-a282-bf7d3391b8b9.xml +40 -0
- data/test/stub_responses/records/contact-e1826204-cc0a-42a5-a6d0-4b352d9d5953.xml +40 -0
- data/test/stub_responses/records/contact-e2d955db-f366-42dd-87f7-fbdb4da2306f.xml +40 -0
- data/test/stub_responses/records/contact-e32e2130-3d27-443a-8313-48fffa03cf53.xml +40 -0
- data/test/stub_responses/records/contact-e3a68332-d322-4816-8678-73a537c8cd33.xml +38 -0
- data/test/stub_responses/records/contact-e6ac76a3-ca32-4fa1-8ef9-6a4bf8b0ec2a.xml +40 -0
- data/test/stub_responses/records/contact-e6ca965d-7c48-480e-be39-e847307f474a.xml +38 -0
- data/test/stub_responses/records/contact-e77d1f20-2e8e-46ec-9a10-50335a216724.xml +40 -0
- data/test/stub_responses/records/contact-e8b98c13-a424-41d2-ba0e-7b7621411e7a.xml +38 -0
- data/test/stub_responses/records/contact-e8e9a2c2-3e7e-48ed-8528-c3d61b28f276.xml +39 -0
- data/test/stub_responses/records/contact-eb43fcc6-87ec-4a0a-b243-d718bee4e2cb.xml +38 -0
- data/test/stub_responses/records/contact-ef6f54c1-eb45-4956-b8cd-1be82ad665f2.xml +43 -0
- data/test/stub_responses/records/contact-efdb3600-f233-42e2-8f18-ce7e2a95e4b1.xml +38 -0
- data/test/stub_responses/records/contact-f7eca431-5c97-4d24-93fd-004bb8a6c644.xml +40 -0
- data/test/stub_responses/records/contact-fb078879-5d6d-474f-825f-61dc90689349.xml +38 -0
- data/test/stub_responses/records/contact-fc39b273-4aa2-4785-99ca-24672f6c0000.xml +38 -0
- data/test/stub_responses/records/contact-fc9ec3a6-a2fe-4300-a8cb-ca8a0b3662e0.xml +40 -0
- data/test/stub_responses/records/contact-fdf96102-7491-44b6-bf4d-7a77ff25f890.xml +40 -0
- data/test/stub_responses/records/contact-fe61ead1-8afc-4f0b-beda-066620227aad.xml +38 -0
- data/test/stub_responses/records/credit_note-371cd138-1e5c-4ec1-a8c6-a1c10e8bdab1.xml +73 -0
- data/test/stub_responses/records/credit_note-3bffc09b-79f2-490d-b91b-c59b700b43a4.xml +95 -0
- data/test/stub_responses/records/credit_note-43c678ee-f357-48e2-b192-b6e3634762f9.xml +94 -0
- data/test/stub_responses/records/credit_note-482c018b-d329-4e05-9b4f-7a4cfc695aa0.xml +77 -0
- data/test/stub_responses/records/credit_note-4f67130a-749a-4ee6-98b2-743adbc11245.xml +62 -0
- data/test/stub_responses/records/credit_note-50e98404-2fba-4031-af67-8ba4bb227c44.xml +77 -0
- data/test/stub_responses/records/credit_note-7df8949c-b71f-40c0-bbcf-39f2f450f286.xml +75 -0
- data/test/stub_responses/records/credit_note-b356e488-2678-4be4-ad4b-d294df2d48d6.xml +78 -0
- data/test/stub_responses/records/invoice-0032f627-3156-4d30-9b1c-4d3b994dc921.xml +84 -0
- data/test/stub_responses/records/invoice-00c9511b-24b9-4190-a90a-8abf2fe9f4a0.xml +76 -0
- data/test/stub_responses/records/invoice-024d7994-a26c-4c20-9894-13934840fc31.xml +74 -0
- data/test/stub_responses/records/invoice-0e64a623-c2a1-446a-93ed-eb897f118cbc.xml +97 -0
- data/test/stub_responses/records/invoice-15e88e57-2554-4496-a18e-eb3f5c622345.xml +75 -0
- data/test/stub_responses/records/invoice-166f0588-d0ba-458c-b28a-8edd4c8fc463.xml +75 -0
- data/test/stub_responses/records/invoice-1d1ba340-afa2-4f4c-8ff7-a147bda9a47b.xml +93 -0
- data/test/stub_responses/records/invoice-290ef4c4-baec-492b-b4dd-c102826470ae.xml +87 -0
- data/test/stub_responses/records/invoice-30a87092-31b5-4a2c-831e-327486533dd2.xml +79 -0
- data/test/stub_responses/records/invoice-30dbd181-72a8-43df-b392-4241bf43d5fc.xml +79 -0
- data/test/stub_responses/records/invoice-33e4123e-7cdd-4f05-9a0a-eb8adeb2b868.xml +94 -0
- data/test/stub_responses/records/invoice-387db692-26ac-47e6-b6cc-015343809bda.xml +75 -0
- data/test/stub_responses/records/invoice-3b28bf11-ed2f-4cf4-8e9e-fcae730cc292.xml +91 -0
- data/test/stub_responses/records/invoice-3fcb9847-b350-412e-ab90-7d9d774ad881.xml +91 -0
- data/test/stub_responses/records/invoice-440613e4-4785-4eff-9ba1-c432816cf8c7.xml +78 -0
- data/test/stub_responses/records/invoice-4602eda6-abe9-448e-b65f-ae6bea21f0eb.xml +98 -0
- data/test/stub_responses/records/invoice-46441f63-873f-4cdc-a278-b8fe516f3abb.xml +94 -0
- data/test/stub_responses/records/invoice-4ad1ec01-f4a3-41d7-bbb4-d2ab2fec8e65.xml +74 -0
- data/test/stub_responses/records/invoice-4b9afceb-f7c7-4e64-8aac-7b009971fd52.xml +86 -0
- data/test/stub_responses/records/invoice-4edbf6d5-4e92-43af-bedd-7effc0b86833.xml +70 -0
- data/test/stub_responses/records/invoice-4fad1af2-b871-4ac5-a15a-3c5e32d2e2c4.xml +93 -0
- data/test/stub_responses/records/invoice-52ee4d67-cae4-462c-adb2-182c39017f3d.xml +83 -0
- data/test/stub_responses/records/invoice-54585f46-c1a0-4432-bd4f-c1fae2fba59b.xml +67 -0
- data/test/stub_responses/records/invoice-5613938b-9e27-472e-92ae-3b038b669d10.xml +87 -0
- data/test/stub_responses/records/invoice-5aa9451d-95d1-4f95-a966-bbab2573f71c.xml +75 -0
- data/test/stub_responses/records/invoice-5aadcd34-01a9-4b8d-a2bb-d7cc1de9fa45.xml +89 -0
- data/test/stub_responses/records/invoice-5f6deadf-36a2-495a-9980-ceb11e8af9a9.xml +85 -0
- data/test/stub_responses/records/invoice-625ffe1b-f5d8-438e-a376-981de5f5a733.xml +77 -0
- data/test/stub_responses/records/invoice-64cd559e-8e03-46af-b461-8555285cee71.xml +86 -0
- data/test/stub_responses/records/invoice-666f8dbb-bc9a-476c-8ec4-4665d7f83190.xml +63 -0
- data/test/stub_responses/records/invoice-66fbe37f-49b1-43fd-97ed-85114022cd2f.xml +79 -0
- data/test/stub_responses/records/invoice-673dd7cc-beb7-4697-83d4-0c47cb400cc2.xml +95 -0
- data/test/stub_responses/records/invoice-69fc971e-9b37-41c5-9c87-174330f22343.xml +66 -0
- data/test/stub_responses/records/invoice-70e6db69-e5a4-42c7-a397-aa3212c2945f.xml +75 -0
- data/test/stub_responses/records/invoice-762aa45d-4632-45b5-8087-b4f47690665e.xml +54 -0
- data/test/stub_responses/records/invoice-766d1289-b440-4675-a656-1a0612ecac77.xml +76 -0
- data/test/stub_responses/records/invoice-76bcb361-f93b-4513-b312-5a4af306d276.xml +67 -0
- data/test/stub_responses/records/invoice-76e3f056-479f-417c-a72b-f3d767899b87.xml +91 -0
- data/test/stub_responses/records/invoice-77b338ef-ecc0-4b95-a0d7-2617b0054611.xml +114 -0
- data/test/stub_responses/records/invoice-7be9956d-5316-4f6b-a66a-d355b3f159b2.xml +84 -0
- data/test/stub_responses/records/invoice-7dae876a-b424-436b-a4e6-17b3fdeec80c.xml +91 -0
- data/test/stub_responses/records/invoice-7e862d93-8dab-4856-8b0c-d844e09d750f.xml +68 -0
- data/test/stub_responses/records/invoice-803f70b0-56d9-4157-9787-41df271777a0.xml +84 -0
- data/test/stub_responses/records/invoice-86102312-aa3f-438c-9938-6840f4d8dda6.xml +75 -0
- data/test/stub_responses/records/invoice-8694c9c5-7097-4449-a708-b8c1982921a4.xml +69 -0
- data/test/stub_responses/records/invoice-86d6e00f-ef56-49f7-9a54-796ccd5ca057.xml +85 -0
- data/test/stub_responses/records/invoice-88e77f0f-54a5-4efc-a979-7e22223cc4d7.xml +66 -0
- data/test/stub_responses/records/invoice-8b0ccb6a-d9b7-4da5-8360-ef7fb157b5aa.xml +66 -0
- data/test/stub_responses/records/invoice-935fc854-8037-4111-8d91-993010c331cc.xml +74 -0
- data/test/stub_responses/records/invoice-95ef3000-c764-4ba9-a66a-b6e2d161f839.xml +62 -0
- data/test/stub_responses/records/invoice-962ef33f-c9d2-4602-9b9f-93a02bea23b3.xml +93 -0
- data/test/stub_responses/records/invoice-9868b472-1983-48e9-8edf-7e81ddf2c03a.xml +85 -0
- data/test/stub_responses/records/invoice-9a448e9b-a9fa-4a8b-98f5-6dc892a37374.xml +62 -0
- data/test/stub_responses/records/invoice-a1d04a14-96a8-4067-a0ff-8136990a354f.xml +91 -0
- data/test/stub_responses/records/invoice-a3bc62ef-f11b-4a9c-a4f9-a342bda371b5.xml +66 -0
- data/test/stub_responses/records/invoice-a6894ca0-60ee-4d45-9dd4-b44fcba46ec5.xml +90 -0
- data/test/stub_responses/records/invoice-a77268ce-74b2-483d-a2b3-70dbdc9e49d2.xml +78 -0
- data/test/stub_responses/records/invoice-a9f765e6-b9bc-4505-a47b-fb3ecb327e7b.xml +88 -0
- data/test/stub_responses/records/invoice-aa0173af-8707-4e7f-8dde-4c7a357bd312.xml +109 -0
- data/test/stub_responses/records/invoice-ab63738a-370a-43a5-bfa3-620d684e66d0.xml +83 -0
- data/test/stub_responses/records/invoice-b0344791-5a8a-40dd-a208-d99a461a6c10.xml +84 -0
- data/test/stub_responses/records/invoice-b1e53910-473c-46a3-b3cb-38ece571220e.xml +76 -0
- data/test/stub_responses/records/invoice-b2c02d0b-41a8-4d4d-97d7-014c78b3547d.xml +93 -0
- data/test/stub_responses/records/invoice-b75b3928-ab72-4424-8b93-9cdbbde4cd72.xml +90 -0
- data/test/stub_responses/records/invoice-bcd8a71f-aa31-4d0f-8a01-13ea26363ddf.xml +94 -0
- data/test/stub_responses/records/invoice-bfbb7c45-de02-45e7-b065-d9863ecfb0d8.xml +66 -0
- data/test/stub_responses/records/invoice-c12aff7e-12bf-4185-8702-460929f19674.xml +77 -0
- data/test/stub_responses/records/invoice-c3380b96-976d-4b3e-8b26-8d01eb6a3742.xml +87 -0
- data/test/stub_responses/records/invoice-c963f2b0-cbe1-4abd-9ccc-7e512c942068.xml +67 -0
- data/test/stub_responses/records/invoice-cba46b29-3788-4158-b668-ab10160ccbfe.xml +78 -0
- data/test/stub_responses/records/invoice-d62646b9-d0a9-4fdb-9561-756a8b7eba45.xml +63 -0
- data/test/stub_responses/records/invoice-dba2f021-f149-4191-a126-5351d587ab0e.xml +75 -0
- data/test/stub_responses/records/invoice-de5d9c29-21b3-4342-958b-ed72c4bd7ab0.xml +108 -0
- data/test/stub_responses/records/invoice-e3d96555-2876-4364-a46a-7551a4f52611.xml +123 -0
- data/test/stub_responses/records/invoice-e4a0afbd-aea0-450b-ae23-0ce921e84a77.xml +102 -0
- data/test/stub_responses/records/invoice-e9cb9ecb-58ef-43a8-bd20-69a85338142d.xml +76 -0
- data/test/stub_responses/records/invoice-ec9a6f67-7128-4a63-8ba3-5e516f455f9b.xml +94 -0
- data/test/stub_responses/records/invoice-ed0f2587-84fc-4aef-bc4b-b1a262e24484.xml +81 -0
- data/test/stub_responses/records/invoice-f362ca53-8ade-4047-865a-bb64bee5863d.xml +74 -0
- data/test/stub_responses/records/invoice-f571c38b-5be1-41e1-ad5a-ff6184284beb.xml +114 -0
- data/test/stub_responses/records/invoice-f5832195-5cd3-4660-ad3f-b73d9c64f263.xml +85 -0
- data/test/stub_responses/records/invoice-f9c857eb-64cd-4235-a078-d04b52c77ea7.xml +76 -0
- data/test/stub_responses/records/manual_journal-4765d07b-aa03-4e56-9166-50661958c864.xml +38 -0
- data/test/stub_responses/records/manual_journal-53fc5558-5b76-4ecd-ae5c-c4af3ccde87c.xml +31 -0
- data/test/stub_responses/records/manual_journal-bb6cfcfc-4500-4475-bd3a-93ee512428e0.xml +31 -0
- data/test/stub_responses/records/manual_journal-f00a355b-7374-445c-886b-0437bea4095c.xml +45 -0
- data/test/stub_responses/refresh_responses.rb +29 -0
- data/test/stub_responses/reports/trial_balance.xml +1435 -0
- data/test/stub_responses/tax_rates.xml +118 -0
- data/test/stub_responses/token_expired +1 -0
- data/test/stub_responses/tracking_categories.xml +27 -0
- data/test/stub_responses/unknown_error.xml +1 -0
- data/test/test_helper.rb +67 -0
- data/test/unit/models/bank_transaction_model_parsing_test.rb +133 -0
- data/test/unit/models/bank_transaction_test.rb +47 -0
- data/test/unit/models/bank_transaction_validation_test.rb +87 -0
- data/test/unit/models/contact_test.rb +26 -0
- data/test/unit/models/credit_note_test.rb +37 -0
- data/test/unit/models/invoice_test.rb +113 -0
- data/test/unit/models/journal_line_test.rb +22 -0
- data/test/unit/models/line_item_sum_test.rb +24 -0
- data/test/unit/models/line_item_test.rb +70 -0
- data/test/unit/oauth_config_test.rb +20 -0
- data/test/unit/oauth_test.rb +97 -0
- data/test/unit/private_application_test.rb +20 -0
- data/test/unit/record/base_model_test.rb +50 -0
- data/test/unit/record/base_test.rb +94 -0
- data/test/unit/record/block_validator_test.rb +125 -0
- data/test/unit/record/model_definition_test.rb +159 -0
- data/test/unit/record/parse_where_hash_test.rb +63 -0
- data/test/unit/record/record_association_test.rb +93 -0
- data/test/unit/record/validators_test.rb +228 -0
- data/test/unit/record_definition_test.rb +27 -0
- data/test/unit/report_definition_test.rb +26 -0
- data/test/unit/report_test.rb +145 -0
- data/xeroizer.gemspec +405 -0
- metadata +561 -0
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
require 'xeroizer/record/base_model_http_proxy'
|
|
2
|
+
|
|
3
|
+
module Xeroizer
|
|
4
|
+
module Record
|
|
5
|
+
|
|
6
|
+
class BaseModel
|
|
7
|
+
|
|
8
|
+
include ClassLevelInheritableAttributes
|
|
9
|
+
class_inheritable_attributes :api_controller_name
|
|
10
|
+
|
|
11
|
+
class InvaidPermissionError < StandardError; end
|
|
12
|
+
ALLOWED_PERMISSIONS = [:read, :write, :update]
|
|
13
|
+
class_inheritable_attributes :permissions
|
|
14
|
+
|
|
15
|
+
class_inheritable_attributes :xml_root_name
|
|
16
|
+
class_inheritable_attributes :optional_xml_root_name
|
|
17
|
+
class_inheritable_attributes :xml_node_name
|
|
18
|
+
|
|
19
|
+
include BaseModelHttpProxy
|
|
20
|
+
|
|
21
|
+
attr_reader :application
|
|
22
|
+
attr_reader :model_name
|
|
23
|
+
attr :model_class
|
|
24
|
+
attr_reader :response
|
|
25
|
+
|
|
26
|
+
class << self
|
|
27
|
+
|
|
28
|
+
# Method to allow override of the default controller name used
|
|
29
|
+
# in the API URLs.
|
|
30
|
+
#
|
|
31
|
+
# Default: pluaralized model name (e.g. if the controller name is
|
|
32
|
+
# Invoice then the default is Invoices.
|
|
33
|
+
def set_api_controller_name(controller_name)
|
|
34
|
+
self.api_controller_name = controller_name
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Set the permissions allowed for this class type.
|
|
38
|
+
# There are no permissions set by default.
|
|
39
|
+
# Valid permissions are :read, :write, :update.
|
|
40
|
+
def set_permissions(*args)
|
|
41
|
+
self.permissions = {}
|
|
42
|
+
args.each do | permission |
|
|
43
|
+
raise InvaidPermissionError.new("Permission #{permission} is invalid.") unless ALLOWED_PERMISSIONS.include?(permission)
|
|
44
|
+
self.permissions[permission] = true
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Method to allow override of the default XML node name.
|
|
49
|
+
#
|
|
50
|
+
# Default: singularized model name in camel-case.
|
|
51
|
+
def set_xml_node_name(node_name)
|
|
52
|
+
self.xml_node_name = node_name
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Method to allow override of the default XML root name to use
|
|
56
|
+
# in has_many associations.
|
|
57
|
+
def set_xml_root_name(root_name)
|
|
58
|
+
self.xml_root_name = root_name
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Method to add an extra top-level node to use in has_many associations.
|
|
62
|
+
def set_optional_xml_root_name(optional_root_name)
|
|
63
|
+
self.optional_root_name = optional_root_name
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
public
|
|
69
|
+
|
|
70
|
+
def initialize(application, model_name)
|
|
71
|
+
@application = application
|
|
72
|
+
@model_name = model_name
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Retrieve the controller name.
|
|
76
|
+
#
|
|
77
|
+
# Default: pluaralized model name (e.g. if the controller name is
|
|
78
|
+
# Invoice then the default is Invoices.
|
|
79
|
+
def api_controller_name
|
|
80
|
+
self.class.api_controller_name || model_name.pluralize
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def model_class
|
|
84
|
+
@model_class ||= Xeroizer::Record.const_get(model_name.to_sym)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Build a record with attributes set to the value of attributes.
|
|
88
|
+
def build(attributes = {})
|
|
89
|
+
model_class.build(attributes, self)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Create (build and save) a record with attributes set to the value of attributes.
|
|
93
|
+
def create(attributes = {})
|
|
94
|
+
build(attributes).tap { |resource| resource.save }
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Retreive full record list for this model.
|
|
98
|
+
def all(options = {})
|
|
99
|
+
raise MethodNotAllowed.new(self, :all) unless self.class.permissions[:read]
|
|
100
|
+
response_xml = http_get(parse_params(options))
|
|
101
|
+
response = parse_response(response_xml, options)
|
|
102
|
+
response.response_items || []
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Helper method to retrieve just the first element from
|
|
106
|
+
# the full record list.
|
|
107
|
+
def first(options = {})
|
|
108
|
+
raise MethodNotAllowed.new(self, :all) unless self.class.permissions[:read]
|
|
109
|
+
result = all(options)
|
|
110
|
+
result.first if result.is_a?(Array)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Retrieve record matching the passed in ID.
|
|
114
|
+
def find(id, options = {})
|
|
115
|
+
raise MethodNotAllowed.new(self, :all) unless self.class.permissions[:read]
|
|
116
|
+
response_xml = @application.http_get(@application.client, "#{url}/#{CGI.escape(id)}", options)
|
|
117
|
+
response = parse_response(response_xml, options)
|
|
118
|
+
result = response.response_items.first if response.response_items.is_a?(Array)
|
|
119
|
+
result.complete_record_downloaded = true if result
|
|
120
|
+
result
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def parse_response(response_xml, options = {})
|
|
124
|
+
Response.parse(response_xml, options) do | response, elements, response_model_name |
|
|
125
|
+
if model_name == response_model_name
|
|
126
|
+
@response = response
|
|
127
|
+
parse_records(response, elements)
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
protected
|
|
133
|
+
|
|
134
|
+
# Parse the records part of the XML response and builds model instances as necessary.
|
|
135
|
+
def parse_records(response, elements)
|
|
136
|
+
elements.each do | element |
|
|
137
|
+
response.response_items << model_class.build_from_node(element, self)
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
end
|
|
144
|
+
end
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
require 'xeroizer/application_http_proxy'
|
|
2
|
+
|
|
3
|
+
module Xeroizer
|
|
4
|
+
module Record
|
|
5
|
+
module BaseModelHttpProxy
|
|
6
|
+
|
|
7
|
+
def self.included(base)
|
|
8
|
+
base.send :include, Xeroizer::ApplicationHttpProxy
|
|
9
|
+
base.send :include, InstanceMethods
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
module InstanceMethods
|
|
13
|
+
|
|
14
|
+
protected
|
|
15
|
+
|
|
16
|
+
# Parse parameters for GET requests.
|
|
17
|
+
def parse_params(options)
|
|
18
|
+
params = {}
|
|
19
|
+
params[:ModifiedAfter] = options[:modified_since] if options[:modified_since]
|
|
20
|
+
params[:OrderBy] = options[:order] if options[:order]
|
|
21
|
+
if options[:where]
|
|
22
|
+
params[:where] = case options[:where]
|
|
23
|
+
when String then options[:where]
|
|
24
|
+
when Hash then parse_where_hash(options[:where])
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
params[:offset] = options[:offset] if options[:offset]
|
|
28
|
+
params
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Parse the :where part of the options for GET parameters and construct a valid
|
|
32
|
+
# .Net version of the criteria to pass to Xero.
|
|
33
|
+
#
|
|
34
|
+
# Attribute names can be modified as follows to change the expression used:
|
|
35
|
+
# {attribute_name}_is_greater_than or {attribute_name}> uses '>'
|
|
36
|
+
# {attribute_name}_is_greater_than_or_equal_to or {attribute_name}>= uses '>='
|
|
37
|
+
# {attribute_name}_is_less_than or {attribute_name}< uses '<'
|
|
38
|
+
# {attribute_name}_is_less_than_or_equal_to or {attribute_name}< uses '<='
|
|
39
|
+
# DEFAULT: '=='
|
|
40
|
+
def parse_where_hash(where)
|
|
41
|
+
conditions = []
|
|
42
|
+
where.each do | key, value |
|
|
43
|
+
(attribute_name, expression) = extract_expression_from_attribute_name(key)
|
|
44
|
+
(field_name, field) = model_class.fields.find { | k, v | v[:internal_name] == attribute_name }
|
|
45
|
+
if field
|
|
46
|
+
conditions << where_condition_part(field, expression, value)
|
|
47
|
+
else
|
|
48
|
+
raise InvalidAttributeInWhere.new(model_name, attribute_name)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
conditions.map { | (attr, expression, value) | "#{attr}#{expression}#{value}"}.join('&&')
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Extract the attribute name and expression from the attribute.
|
|
55
|
+
#
|
|
56
|
+
# @return [Array] containing [actual_attribute_name, expression]
|
|
57
|
+
def extract_expression_from_attribute_name(key)
|
|
58
|
+
case key.to_s
|
|
59
|
+
when /(_is_not|\<\>)$/
|
|
60
|
+
[
|
|
61
|
+
key.to_s.gsub(/(_is_not|\<\>)$/, '').to_sym,
|
|
62
|
+
'<>'
|
|
63
|
+
]
|
|
64
|
+
|
|
65
|
+
when /(_is_greater_than|\>)$/
|
|
66
|
+
[
|
|
67
|
+
key.to_s.gsub(/(_is_greater_than|\>)$/, '').to_sym,
|
|
68
|
+
'>'
|
|
69
|
+
]
|
|
70
|
+
|
|
71
|
+
when /(_is_greater_than_or_equal_to|\>\=)$/
|
|
72
|
+
[
|
|
73
|
+
key.to_s.gsub(/(_is_greater_than_or_equal_to|\>\=)$/, '').to_sym,
|
|
74
|
+
'>='
|
|
75
|
+
]
|
|
76
|
+
|
|
77
|
+
when /(_is_less_than|\<)$/
|
|
78
|
+
[
|
|
79
|
+
key.to_s.gsub(/(_is_less_than|\<)$/, '').to_sym,
|
|
80
|
+
'<'
|
|
81
|
+
]
|
|
82
|
+
|
|
83
|
+
when /(_is_less_than_or_equal_to|\<\=)$/
|
|
84
|
+
[
|
|
85
|
+
key.to_s.gsub(/(_is_less_than_or_equal_to|\<\=)$/, '').to_sym,
|
|
86
|
+
'<='
|
|
87
|
+
]
|
|
88
|
+
|
|
89
|
+
else
|
|
90
|
+
[key, '==']
|
|
91
|
+
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Creates a condition part array containing the:
|
|
96
|
+
# * Field's API name
|
|
97
|
+
# * Expression
|
|
98
|
+
# * .Net formatted value.
|
|
99
|
+
def where_condition_part(field, expression, value)
|
|
100
|
+
case field[:type]
|
|
101
|
+
when :guid then ["#{field[:api_name]}.ToString()", expression, "\"#{value}\""]
|
|
102
|
+
when :string then [field[:api_name], expression, "\"#{value}\""]
|
|
103
|
+
when :boolean then [field[:api_name], expression, value ? 'true' : 'false']
|
|
104
|
+
when :integer then [field[:api_name], expression, value.to_s]
|
|
105
|
+
when :decimal then [field[:api_name], expression, value.to_s]
|
|
106
|
+
when :date then [field[:api_name], expression, "DateTime.Parse(\"#{value.strftime("%Y-%m-%d")}\")"]
|
|
107
|
+
when :datetime then [field[:api_name], expression, "DateTime.Parse(\"#{value.utc.strftime("%Y-%m-%dT%H:%M:%S")}\")"]
|
|
108
|
+
when :belongs_to then
|
|
109
|
+
when :has_many then
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
module Xeroizer
|
|
2
|
+
module Record
|
|
3
|
+
module ModelDefinitionHelper
|
|
4
|
+
|
|
5
|
+
def self.included(base)
|
|
6
|
+
base.extend(ClassMethods)
|
|
7
|
+
base.send :include, InstanceMethods
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
module ClassMethods
|
|
11
|
+
|
|
12
|
+
# Possible primary keys. At least one of these must exist before attempting to update a record.
|
|
13
|
+
def set_possible_primary_keys(*args)
|
|
14
|
+
args = [args] unless args.is_a?(Array)
|
|
15
|
+
self.possible_primary_keys = args
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Set the actual Xero primary key for this record.
|
|
19
|
+
def set_primary_key(primary_key_name)
|
|
20
|
+
self.primary_key_name = primary_key_name
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Whether this record type's list results contain summary data only.
|
|
24
|
+
#
|
|
25
|
+
# Records like invoices, when returning a list, only show summary data for
|
|
26
|
+
# certain types of associations (like the contact record) and do not return
|
|
27
|
+
# any data for line items.
|
|
28
|
+
#
|
|
29
|
+
# Default: false
|
|
30
|
+
def list_contains_summary_only(status)
|
|
31
|
+
self.summary_only = status
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def list_contains_summary_only?
|
|
35
|
+
!!summary_only
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Helper methods used to define the fields this model has.
|
|
39
|
+
def string(field_name, options = {}); define_simple_attribute(field_name, :string, options); end
|
|
40
|
+
def boolean(field_name, options = {}); define_simple_attribute(field_name, :boolean, options); end
|
|
41
|
+
def integer(field_name, options = {}); define_simple_attribute(field_name, :integer, options, 0); end
|
|
42
|
+
def decimal(field_name, options = {}); define_simple_attribute(field_name, :decimal, options, 0.0); end
|
|
43
|
+
def date(field_name, options = {}); define_simple_attribute(field_name, :date, options); end
|
|
44
|
+
def datetime(field_name, options = {}); define_simple_attribute(field_name, :datetime, options); end
|
|
45
|
+
def datetime_utc(field_name, options = {}); define_simple_attribute(field_name, :datetime_utc, options); end
|
|
46
|
+
|
|
47
|
+
def guid(field_name, options = {})
|
|
48
|
+
# Ensure all automated Id conversions are changed to ID.
|
|
49
|
+
options[:api_name] ||= field_name.to_s.camelize.gsub(/Id/, 'ID')
|
|
50
|
+
define_simple_attribute(field_name, :guid, options)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Helper method to simplify field definition.
|
|
54
|
+
# Creates an accessor and reader for the field.
|
|
55
|
+
# Options:
|
|
56
|
+
# :internal_name => allows the specification of an internal field name differing from the API's field name.
|
|
57
|
+
# :api_name => allows the API name to be specified if it can't be properly converted from camelize.
|
|
58
|
+
# :model_name => allows class used for children to be different from it's ndoe name in the XML.
|
|
59
|
+
# :type => type of field
|
|
60
|
+
# :skip_writer => skip the writer method
|
|
61
|
+
def define_simple_attribute(field_name, field_type, options, value_if_nil = nil)
|
|
62
|
+
self.fields ||= {}
|
|
63
|
+
|
|
64
|
+
internal_field_name = options[:internal_name] || field_name
|
|
65
|
+
self.fields[field_name] = options.merge({
|
|
66
|
+
:internal_name => internal_field_name,
|
|
67
|
+
:api_name => options[:api_name] || field_name.to_s.camelize,
|
|
68
|
+
:type => field_type
|
|
69
|
+
})
|
|
70
|
+
define_method internal_field_name do
|
|
71
|
+
@attributes[field_name] || value_if_nil
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
unless options[:skip_writer]
|
|
75
|
+
define_method "#{internal_field_name}=".to_sym do | value |
|
|
76
|
+
@attributes[field_name] = value
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
module InstanceMethods
|
|
84
|
+
|
|
85
|
+
# Returns the value of the Xero primary key for this record if it exists.
|
|
86
|
+
def id
|
|
87
|
+
self[self.class.primary_key_name]
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Sets the value of the Xero primary key for this record if it exists.
|
|
91
|
+
def id=(new_id)
|
|
92
|
+
self[self.class.primary_key_name] = new_id
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
module Xeroizer
|
|
2
|
+
module Record
|
|
3
|
+
module RecordAssociationHelper
|
|
4
|
+
|
|
5
|
+
def self.included(base)
|
|
6
|
+
base.extend(ClassMethods)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
module ClassMethods
|
|
10
|
+
|
|
11
|
+
def belongs_to(field_name, options = {})
|
|
12
|
+
internal_field_name = options[:internal_name] || field_name
|
|
13
|
+
internal_singular_field_name = internal_field_name.to_s.singularize
|
|
14
|
+
|
|
15
|
+
define_association_attribute(field_name, internal_singular_field_name, :belongs_to, options)
|
|
16
|
+
|
|
17
|
+
# Create a #build_record_name method to build the record.
|
|
18
|
+
define_method "build_#{internal_singular_field_name}" do | *args |
|
|
19
|
+
attributes = args.size == 1 ? args.first : {}
|
|
20
|
+
|
|
21
|
+
# The name of the record model.
|
|
22
|
+
model_name = options[:model_name] ? options[:model_name].to_sym : field_name.to_s.singularize.camelize.to_sym
|
|
23
|
+
|
|
24
|
+
# The record's parent instance for this current application.
|
|
25
|
+
model_parent = new_model_class(model_name)
|
|
26
|
+
|
|
27
|
+
# Create a new record, binding it to it's parent instance.
|
|
28
|
+
record = Xeroizer::Record.const_get(model_name).build(attributes, model_parent)
|
|
29
|
+
self.attributes[field_name] = record
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def has_many(field_name, options = {})
|
|
34
|
+
internal_field_name = options[:internal_name] || field_name
|
|
35
|
+
internal_singular_field_name = internal_field_name.to_s.singularize
|
|
36
|
+
|
|
37
|
+
define_association_attribute(field_name, internal_field_name, :has_many, options)
|
|
38
|
+
|
|
39
|
+
# Create an #add_record_name method to build the record and add to the attributes.
|
|
40
|
+
define_method "add_#{internal_singular_field_name}" do | *args |
|
|
41
|
+
# The name of the record model.
|
|
42
|
+
model_name = options[:model_name] ? options[:model_name].to_sym : field_name.to_s.singularize.camelize.to_sym
|
|
43
|
+
|
|
44
|
+
# The record's parent instance for this current application.
|
|
45
|
+
model_parent = new_model_class(model_name)
|
|
46
|
+
|
|
47
|
+
# The class of this record.
|
|
48
|
+
record_class = Xeroizer::Record.const_get(model_name)
|
|
49
|
+
|
|
50
|
+
# Parse the *args variable so that we can use this method like:
|
|
51
|
+
# add_record({fields}, {fields}, ...)
|
|
52
|
+
# add_record(record_one, record_two, ...)
|
|
53
|
+
# add_record([{fields}, {fields}], ...)
|
|
54
|
+
# add_record(key => val, key2 => val)
|
|
55
|
+
records = []
|
|
56
|
+
if args.size == 1 && args.first.is_a?(Array)
|
|
57
|
+
records = args.first
|
|
58
|
+
elsif args.size > 0
|
|
59
|
+
records = args
|
|
60
|
+
else
|
|
61
|
+
raise StandardError.new("Invalid arguments for #{self.class.name}#add_#{internal_singular_field_name}(#{args.inspect}).")
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Ensure that complete record is downloaded before adding new records
|
|
65
|
+
self.send(field_name)
|
|
66
|
+
|
|
67
|
+
# Add each record.
|
|
68
|
+
last_record = nil
|
|
69
|
+
records.each do | record |
|
|
70
|
+
record = record_class.build(record, model_parent) if record.is_a?(Hash)
|
|
71
|
+
raise StandardError.new("Record #{record.class.name} is not a #{record_class.name}.") unless record.is_a?(record_class)
|
|
72
|
+
self.attributes[field_name] ||= []
|
|
73
|
+
self.attributes[field_name] << record
|
|
74
|
+
last_record = record
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
last_record # last record
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def define_association_attribute(field_name, internal_field_name, association_type, options)
|
|
83
|
+
define_simple_attribute(field_name, association_type, options.merge!(:skip_writer => true), ((association_type == :has_many) ? [] : nil))
|
|
84
|
+
|
|
85
|
+
internal_field_name = options[:internal_name] || field_name
|
|
86
|
+
internal_singular_field_name = internal_field_name.to_s.singularize
|
|
87
|
+
model_name = options[:model_name] ? options[:model_name].to_sym : field_name.to_s.singularize.camelize.to_sym
|
|
88
|
+
|
|
89
|
+
define_method "#{internal_field_name}=".to_sym do | value |
|
|
90
|
+
record_class = Xeroizer::Record.const_get(model_name)
|
|
91
|
+
case value
|
|
92
|
+
when Hash
|
|
93
|
+
self.attributes[field_name] = ((association_type == :has_many) ? [] : nil)
|
|
94
|
+
case association_type
|
|
95
|
+
when :has_many
|
|
96
|
+
self.attributes[field_name] = []
|
|
97
|
+
self.send("add_#{internal_singular_field_name}".to_sym, value)
|
|
98
|
+
|
|
99
|
+
when :belongs_to
|
|
100
|
+
self.attributes[field_name] = Xeroizer::Record.const_get(model_name).build(value, new_model_class(model_name))
|
|
101
|
+
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
when Array
|
|
105
|
+
self.attributes[field_name] = ((association_type == :has_many) ? [] : nil)
|
|
106
|
+
value.each do | single_value |
|
|
107
|
+
case single_value
|
|
108
|
+
when Hash then send("add_#{internal_singular_field_name}".to_sym, single_value)
|
|
109
|
+
when record_class then self.attributes[field_name] << single_value
|
|
110
|
+
else raise AssociationTypeMismatch.new(record_class, single_value.class)
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
when record_class
|
|
115
|
+
self.attributes[field_name] = ((association_type == :has_many) ? [value] : value)
|
|
116
|
+
|
|
117
|
+
when NilClass
|
|
118
|
+
self.attributes[field_name] = []
|
|
119
|
+
|
|
120
|
+
else
|
|
121
|
+
raise AssociationTypeMismatch.new(record_class, value.class)
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Override reader for this association if this association belongs
|
|
126
|
+
# to a summary-typed record. This will automatically attempt to download
|
|
127
|
+
# the complete version of the record before accessing the association.
|
|
128
|
+
if list_contains_summary_only?
|
|
129
|
+
define_method internal_field_name do
|
|
130
|
+
download_complete_record! unless new_record? || options[:list_complete] || complete_record_downloaded?
|
|
131
|
+
self.attributes[field_name] || ((association_type == :has_many) ? [] : nil)
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|