From df842809587080d98ed3905bbfa427cb36e3d363 Mon Sep 17 00:00:00 2001 From: Brian Lauer Date: Tue, 4 Nov 2025 10:15:28 -0500 Subject: [PATCH 1/2] Implement migration of Vendor 1099 data from Dynamics SL --- .../Support/SLHelperFunctions.Codeunit.al | 33 + .../SLD365BASICISVMSL.PermissionSetExt.al | 3 + .../SLD365BASICMSL.PermissionSetExt.al | 3 + .../SLD365TEAMMEMBERMSL.PermissionSetExt.al | 3 + .../SLINTELLIGENTCLOUDMSL.PermissionSetExt.al | 3 + .../SLMigrationEdit.PermissionSet.al | 3 + .../SLMigrationObjects.PermissionSet.al | 3 + .../SLMigrationRead.PermissionSet.al | 3 + .../src/codeunits/SLIRSFormData.Codeunit.al | 184 +++++ .../SLPopulateVendor1099Data.Codeunit.al | 766 ++++++++++++++++++ .../SLVendor1099MappingHelpers.Codeunit.al | 101 +++ .../app/src/pages/SL1099MigrationLog.Page.al | 88 ++ .../pages/SLCompanyAddSettingsList.Page.al | 10 + .../pages/SLMigrationConfiguration.Page.al | 38 + .../app/src/tables/SL1099BoxMapping.Table.al | 50 ++ .../src/tables/SL1099MigrationLog.Table.al | 97 +++ .../SLCompanyAdditionalSettings.Table.al | 48 ++ .../src/tables/SLSupportedTaxYear.Table.al | 29 + 18 files changed, 1465 insertions(+) create mode 100644 Apps/W1/HybridSL/app/src/codeunits/SLIRSFormData.Codeunit.al create mode 100644 Apps/W1/HybridSL/app/src/codeunits/SLPopulateVendor1099Data.Codeunit.al create mode 100644 Apps/W1/HybridSL/app/src/codeunits/SLVendor1099MappingHelpers.Codeunit.al create mode 100644 Apps/W1/HybridSL/app/src/pages/SL1099MigrationLog.Page.al create mode 100644 Apps/W1/HybridSL/app/src/tables/SL1099BoxMapping.Table.al create mode 100644 Apps/W1/HybridSL/app/src/tables/SL1099MigrationLog.Table.al create mode 100644 Apps/W1/HybridSL/app/src/tables/SLSupportedTaxYear.Table.al diff --git a/Apps/W1/HybridSL/app/src/Migration/Support/SLHelperFunctions.Codeunit.al b/Apps/W1/HybridSL/app/src/Migration/Support/SLHelperFunctions.Codeunit.al index 158d61f0e2..170dc16386 100644 --- a/Apps/W1/HybridSL/app/src/Migration/Support/SLHelperFunctions.Codeunit.al +++ b/Apps/W1/HybridSL/app/src/Migration/Support/SLHelperFunctions.Codeunit.al @@ -1108,7 +1108,15 @@ codeunit 47023 "SL Helper Functions" internal procedure CreatePostMigrationData(): Boolean var SLCompanyAdditionalSettings: Record "SL Company Additional Settings"; + SLPopulateVendor1099Data: Codeunit "SL Populate Vendor 1099 Data"; begin + if (SLCompanyAdditionalSettings.GetMigrateCurrent1099YearEnabled()) or (SLCompanyAdditionalSettings.GetMigrateNext1099YearEnabled()) then begin + SetupIRSFormsFeatureIfNeeded(); + BindSubscription(SLPopulateVendor1099Data); + SLPopulateVendor1099Data.Run(); + UnbindSubscription(SLPopulateVendor1099Data); + end; + if SLCompanyAdditionalSettings.GetProjectModuleEnabled() then if not ProjectDataCreated() then CreateProjectData(); @@ -1176,6 +1184,31 @@ codeunit 47023 "SL Helper Functions" SLConfiguration.Modify(); end; + internal procedure SetupIRSFormsFeatureIfNeeded() + var + SLCompanyAdditionalSettings: Record "SL Company Additional Settings"; + SLIRSFormData: Codeunit "SL IRS Form Data"; + SLVendor1099MappingHelpers: Codeunit "SL Vendor 1099 Mapping Helpers"; + ReportingYear: Integer; + Open1099Year: Boolean; + begin + SLCompanyAdditionalSettings.GetSingleInstance(); + if SLCompanyAdditionalSettings.GetMigrateCurrent1099YearEnabled() then begin + ReportingYear := SLVendor1099MappingHelpers.GetCurrent1099YearFromSLAPSetup(); + Open1099Year := SLVendor1099MappingHelpers.GetCurrent1099YearOpenStatus(); + if ReportingYear <> 0 then + if Open1099Year then + SLIRSFormData.CreateIRSFormsReportingPeriodIfNeeded(ReportingYear); + end; + if SLCompanyAdditionalSettings.GetMigrateNext1099YearEnabled() then begin + ReportingYear := SLVendor1099MappingHelpers.GetNext1099YearFromSLAPSetup(); + Open1099Year := SLVendor1099MappingHelpers.GetNext1099YearOpenStatus(); + if ReportingYear <> 0 then + if Open1099Year then + SLIRSFormData.CreateIRSFormsReportingPeriodIfNeeded(ReportingYear); + end; + end; + [IntegrationEvent(false, false)] internal procedure OnSkipPostingGLAccounts(var SkipPosting: Boolean) begin diff --git a/Apps/W1/HybridSL/app/src/Permissions/SLD365BASICISVMSL.PermissionSetExt.al b/Apps/W1/HybridSL/app/src/Permissions/SLD365BASICISVMSL.PermissionSetExt.al index 6eef3ef69d..68acd65983 100644 --- a/Apps/W1/HybridSL/app/src/Permissions/SLD365BASICISVMSL.PermissionSetExt.al +++ b/Apps/W1/HybridSL/app/src/Permissions/SLD365BASICISVMSL.PermissionSetExt.al @@ -118,10 +118,13 @@ permissionsetextension 47002 "SLD365 BASIC ISV - MSL" extends "D365 BASIC ISV" tabledata "SL SOShipLine" = RIMD, tabledata "SL SOShipLot" = RIMD, tabledata "SL SOType" = RIMD, + tabledata "SL Supported Tax Year" = RIMD, tabledata "SL Terms" = RIMD, tabledata "SL Upgrade Settings" = RIMD, tabledata "SL VendClass" = RIMD, tabledata "SL Vendor" = RIMD, + tabledata "SL 1099 Box Mapping" = RIMD, + tabledata "SL 1099 Migration Log" = RIMD, tabledata SLGLAcctBalByPeriod = RIMD, tabledata "SL Period List Work Table" = RIMD; } \ No newline at end of file diff --git a/Apps/W1/HybridSL/app/src/Permissions/SLD365BASICMSL.PermissionSetExt.al b/Apps/W1/HybridSL/app/src/Permissions/SLD365BASICMSL.PermissionSetExt.al index 8a769faba5..e16b169c67 100644 --- a/Apps/W1/HybridSL/app/src/Permissions/SLD365BASICMSL.PermissionSetExt.al +++ b/Apps/W1/HybridSL/app/src/Permissions/SLD365BASICMSL.PermissionSetExt.al @@ -118,10 +118,13 @@ permissionsetextension 47001 "SLD365 BASIC - MSL" extends "D365 BASIC" tabledata "SL SOShipLine" = RIMD, tabledata "SL SOShipLot" = RIMD, tabledata "SL SOType" = RIMD, + tabledata "SL Supported Tax Year" = RIMD, tabledata "SL Terms" = RIMD, tabledata "SL Upgrade Settings" = RIMD, tabledata "SL VendClass" = RIMD, tabledata "SL Vendor" = RIMD, + tabledata "SL 1099 Box Mapping" = RIMD, + tabledata "SL 1099 Migration Log" = RIMD, tabledata SLGLAcctBalByPeriod = RIMD, tabledata "SL Period List Work Table" = RIMD; } \ No newline at end of file diff --git a/Apps/W1/HybridSL/app/src/Permissions/SLD365TEAMMEMBERMSL.PermissionSetExt.al b/Apps/W1/HybridSL/app/src/Permissions/SLD365TEAMMEMBERMSL.PermissionSetExt.al index d0b3e5ba0b..38a0bf1d38 100644 --- a/Apps/W1/HybridSL/app/src/Permissions/SLD365TEAMMEMBERMSL.PermissionSetExt.al +++ b/Apps/W1/HybridSL/app/src/Permissions/SLD365TEAMMEMBERMSL.PermissionSetExt.al @@ -118,10 +118,13 @@ permissionsetextension 47003 "SLD365 TEAM MEMBER - MSL" extends "D365 TEAM MEMBE tabledata "SL SOShipLine" = RIMD, tabledata "SL SOShipLot" = RIMD, tabledata "SL SOType" = RIMD, + tabledata "SL Supported Tax Year" = RIMD, tabledata "SL Terms" = RIMD, tabledata "SL Upgrade Settings" = RIMD, tabledata "SL VendClass" = RIMD, tabledata "SL Vendor" = RIMD, + tabledata "SL 1099 Box Mapping" = RIMD, + tabledata "SL 1099 Migration Log" = RIMD, tabledata SLGLAcctBalByPeriod = RIMD, tabledata "SL Period List Work Table" = RIMD; } \ No newline at end of file diff --git a/Apps/W1/HybridSL/app/src/Permissions/SLINTELLIGENTCLOUDMSL.PermissionSetExt.al b/Apps/W1/HybridSL/app/src/Permissions/SLINTELLIGENTCLOUDMSL.PermissionSetExt.al index ef17f50848..aa4aa36530 100644 --- a/Apps/W1/HybridSL/app/src/Permissions/SLINTELLIGENTCLOUDMSL.PermissionSetExt.al +++ b/Apps/W1/HybridSL/app/src/Permissions/SLINTELLIGENTCLOUDMSL.PermissionSetExt.al @@ -118,10 +118,13 @@ permissionsetextension 47000 "SLINTELLIGENT CLOUD - MSL" extends "INTELLIGENT CL tabledata "SL SOShipLine" = RIMD, tabledata "SL SOShipLot" = RIMD, tabledata "SL SOType" = RIMD, + tabledata "SL Supported Tax Year" = RIMD, tabledata "SL Terms" = RIMD, tabledata "SL Upgrade Settings" = RIMD, tabledata "SL VendClass" = RIMD, tabledata "SL Vendor" = RIMD, + tabledata "SL 1099 Box Mapping" = RIMD, + tabledata "SL 1099 Migration Log" = RIMD, tabledata SLGLAcctBalByPeriod = RIMD, tabledata "SL Period List Work Table" = RIMD; } \ No newline at end of file diff --git a/Apps/W1/HybridSL/app/src/Permissions/SLMigrationEdit.PermissionSet.al b/Apps/W1/HybridSL/app/src/Permissions/SLMigrationEdit.PermissionSet.al index c99eeb6d94..2d39a56d88 100644 --- a/Apps/W1/HybridSL/app/src/Permissions/SLMigrationEdit.PermissionSet.al +++ b/Apps/W1/HybridSL/app/src/Permissions/SLMigrationEdit.PermissionSet.al @@ -120,9 +120,12 @@ permissionset 47004 "SL Migration - Edit" tabledata "SL SOShipLine" = IMD, tabledata "SL SOShipLot" = IMD, tabledata "SL SOType" = IMD, + tabledata "SL Supported Tax Year" = IMD, tabledata "SL Terms" = IMD, tabledata "SL Upgrade Settings" = IMD, tabledata "SL VendClass" = IMD, tabledata "SL Vendor" = IMD, + tabledata "SL 1099 Box Mapping" = IMD, + tabledata "SL 1099 Migration Log" = IMD, tabledata SLGLAcctBalByPeriod = IMD; } \ No newline at end of file diff --git a/Apps/W1/HybridSL/app/src/Permissions/SLMigrationObjects.PermissionSet.al b/Apps/W1/HybridSL/app/src/Permissions/SLMigrationObjects.PermissionSet.al index b47323c952..65e7b889e7 100644 --- a/Apps/W1/HybridSL/app/src/Permissions/SLMigrationObjects.PermissionSet.al +++ b/Apps/W1/HybridSL/app/src/Permissions/SLMigrationObjects.PermissionSet.al @@ -121,10 +121,13 @@ permissionset 47005 "SL Migration-Objects" table "SL SOShipLine" = X, table "SL SOShipLot" = X, table "SL SOType" = X, + table "SL Supported Tax Year" = X, table "SL Terms" = X, table "SL Upgrade Settings" = X, table "SL VendClass" = X, table "SL Vendor" = X, + table "SL 1099 Box Mapping" = X, + table "SL 1099 Migration Log" = X, table SLGLAcctBalByPeriod = X, codeunit "SL Account Migrator" = X, codeunit "SL Cloud Migration" = X, diff --git a/Apps/W1/HybridSL/app/src/Permissions/SLMigrationRead.PermissionSet.al b/Apps/W1/HybridSL/app/src/Permissions/SLMigrationRead.PermissionSet.al index b868c04296..c944c3ed2f 100644 --- a/Apps/W1/HybridSL/app/src/Permissions/SLMigrationRead.PermissionSet.al +++ b/Apps/W1/HybridSL/app/src/Permissions/SLMigrationRead.PermissionSet.al @@ -118,9 +118,12 @@ permissionset 47006 "SL Migration - Read" tabledata "SL SOShipLine" = R, tabledata "SL SOShipLot" = R, tabledata "SL SOType" = R, + tabledata "SL Supported Tax Year" = R, tabledata "SL Terms" = R, tabledata "SL Upgrade Settings" = R, tabledata "SL VendClass" = R, tabledata "SL Vendor" = R, + tabledata "SL 1099 Box Mapping" = R, + tabledata "SL 1099 Migration Log" = R, tabledata SLGLAcctBalByPeriod = R; } \ No newline at end of file diff --git a/Apps/W1/HybridSL/app/src/codeunits/SLIRSFormData.Codeunit.al b/Apps/W1/HybridSL/app/src/codeunits/SLIRSFormData.Codeunit.al new file mode 100644 index 0000000000..0996584655 --- /dev/null +++ b/Apps/W1/HybridSL/app/src/codeunits/SLIRSFormData.Codeunit.al @@ -0,0 +1,184 @@ +// ------------------------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ------------------------------------------------------------------------------------------------ + +namespace Microsoft.DataMigration.SL; + +using Microsoft.Finance.VAT.Reporting; + +codeunit 47009 "SL IRS Form Data" +{ + Permissions = tabledata "IRS Reporting Period" = RIM, + tabledata "IRS 1099 Form" = RIM, + tabledata "IRS 1099 Form Box" = RIM, + tabledata "IRS 1099 Form Statement Line" = RIM, + tabledata "IRS 1099 Form Instruction" = RIM; + + var + IRSFormStatementLineFilterExpressionTxt: Label 'Form Box No.: %1', Comment = '%1 = Form Box No.', Locked = true; + + internal procedure CreateIRSFormsReportingPeriodIfNeeded(ReportingYear: Integer) + var + IRSReportingPeriod: Record "IRS Reporting Period"; + PeriodNo: Code[20]; + begin + PeriodNo := Format(ReportingYear); + if not IRSReportingPeriod.Get(PeriodNo) then begin + IRSReportingPeriod.Validate("No.", PeriodNo); + IRSReportingPeriod.Validate("Starting Date", DMY2Date(1, 1, ReportingYear)); + IRSReportingPeriod.Validate("Ending Date", DMY2Date(31, 12, ReportingYear)); + IRSReportingPeriod.Validate("Description", PeriodNo); + IRSReportingPeriod.Insert(true); + + PopulateFormsAndBoxes(PeriodNo); + end; + end; + + local procedure PopulateFormsAndBoxes(PeriodNo: Code[20]) + begin + AddForm(PeriodNo, 'MISC', 'Miscellaneous Income'); + AddFormBox(PeriodNo, 'MISC', 'MISC-01', 'Rents', 600); + AddFormBox(PeriodNo, 'MISC', 'MISC-02', 'Royalties', 10); + AddFormBox(PeriodNo, 'MISC', 'MISC-03', 'Other Income', 600); + AddFormBox(PeriodNo, 'MISC', 'MISC-04', 'Federal Income Tax Withheld', 0); + AddFormBox(PeriodNo, 'MISC', 'MISC-05', 'Fishing Boat Proceeds', 600); + AddFormBox(PeriodNo, 'MISC', 'MISC-06', 'Medical and Health Care Payments', 600); + AddFormBox(PeriodNo, 'MISC', 'MISC-07', 'Payer made direct sales totaling $5,000 or more of consumer products to recipient for resale', 5000); + AddFormBox(PeriodNo, 'MISC', 'MISC-08', 'Substitute Payments in Lieu of Dividends or Interest', 10); + AddFormBox(PeriodNo, 'MISC', 'MISC-09', 'Crop Insurance Proceeds', 1); + AddFormBox(PeriodNo, 'MISC', 'MISC-10', 'Gross Proceeds Paid to an Attorney', 0); + AddFormBox(PeriodNo, 'MISC', 'MISC-11', 'Fish purchased for resale', 600); + AddFormBox(PeriodNo, 'MISC', 'MISC-12', 'Section 409A deferrals', 600); + AddFormBox(PeriodNo, 'MISC', 'MISC-14', 'Excess golden parachute payments', 0); + AddFormBox(PeriodNo, 'MISC', 'MISC-15', 'Nonqualified deferred compensation', 0); + AddFormStatementLine(PeriodNo, 'MISC', 'MISC-01', 10000, 'Rents'); + AddFormStatementLine(PeriodNo, 'MISC', 'MISC-02', 20000, 'Royalties'); + AddFormStatementLine(PeriodNo, 'MISC', 'MISC-03', 30000, 'Other Income'); + AddFormStatementLine(PeriodNo, 'MISC', 'MISC-04', 40000, 'Federal Income Tax Withheld'); + AddFormStatementLine(PeriodNo, 'MISC', 'MISC-05', 50000, 'Fishing Boat Proceeds'); + AddFormStatementLine(PeriodNo, 'MISC', 'MISC-06', 60000, 'Medical and Health Care Payments'); + AddFormStatementLine(PeriodNo, 'MISC', Enum::"IRS 1099 Print Value Type"::"Yes/No", 'MISC-07', 70000, 'Payer made direct sales totaling $5,000 or more of consumer products to recipient for resale'); + AddFormStatementLine(PeriodNo, 'MISC', 'MISC-08', 80000, 'Substitute Payments in Lieu of Dividends or Interest'); + AddFormStatementLine(PeriodNo, 'MISC', 'MISC-09', 90000, 'Crop Insurance Proceeds'); + AddFormStatementLine(PeriodNo, 'MISC', 'MISC-10', 100000, 'Gross Proceeds Paid to an Attorney'); + AddFormStatementLine(PeriodNo, 'MISC', 'MISC-11', 110000, 'Fish purchased for resale'); + AddFormStatementLine(PeriodNo, 'MISC', 'MISC-12', 120000, 'Section 409A deferrals'); + AddFormStatementLine(PeriodNo, 'MISC', 'MISC-14', 130000, 'Excess golden parachute payments'); + AddFormStatementLine(PeriodNo, 'MISC', 'MISC-15', 140000, 'Nonqualified deferred compensation'); + + AddForm(PeriodNo, 'NEC', 'Nonemployee Compensation'); + AddFormBox(PeriodNo, 'NEC', 'NEC-01', 'Nonemployee Compensation', 600); + AddFormStatementLine(PeriodNo, 'NEC', 'NEC-01', 10000, 'Nonemployee Compensation'); + + AddFormInstructionLines(PeriodNo); + end; + + procedure AddFormInstructionLines(PeriodNo: Code[20]) + begin + AddNecFormInstructionLines(PeriodNo); + AddMiscFormInstructionLines(PeriodNo); + end; + + local procedure AddNecFormInstructionLines(PeriodNo: Code[20]) + var + IRS1099Form: Record "IRS 1099 Form"; + begin + if not IRS1099Form.Get(PeriodNo, 'NEC') then + exit; + AddFormInstructionLine(PeriodNo, 'NEC', 1, '', 'You received this form instead of Form W-2 because the payer did not consider you an employee and did not withhold income tax or social security and Medicare tax. If you believe you are an employee and cannot get the payer to correct this form, report the amount shown in box 1 on the line for “Wages, salaries, tips, etc.” of Form 1040, 1040-SR, or 1040-NR. You must also complete Form 8919 and attach it to your return. For more information, see Pub. 1779, Independent Contractor or Employee. If you are not an employee but the amount in box 1 is not self-employment (SE) income (for example, it is income from a sporadic activity or a hobby), report the amount shown in box 1 on the “Other income” line (on Schedule 1 (Form 1040)).'); + AddFormInstructionLine(PeriodNo, 'NEC', 2, 'Recipient’s taxpayer identification number (TIN).', 'For your protection, this form may show only the last four digits of your TIN (social security number (SSN), individual taxpayer identification number (ITIN), adoption taxpayer identification number (ATIN), or employer identification number (EIN)). However, the issuer has reported your complete TIN to the IRS.'); + AddFormInstructionLine(PeriodNo, 'NEC', 3, 'Account number.', 'May show an account or other unique number the payer assigned to distinguish your account.'); + AddFormInstructionLine(PeriodNo, 'NEC', 4, 'Box 1.', 'Shows nonemployee compensation. If the amount in this box is SE income, report it on Schedule C or F (Form 1040) if a sole proprietor, or on Form 1065 and Schedule K-1 (Form 1065) if a partnership, and the recipient/partner completes Schedule SE (Form 1040).'); + AddFormInstructionLine(PeriodNo, 'NEC', 5, 'Note:', 'If you are receiving payments on which no income, social security, and Medicare taxes are withheld, you should make estimated tax payments. See Form 1040-ES (or Form 1040-ES (NR)). Individuals must report these amounts as explained in these box 1 instructions. Corporations, fiduciaries, and partnerships must report these amounts on the appropriate line of their tax returns.'); + AddFormInstructionLine(PeriodNo, 'NEC', 6, 'Box 2.', 'If checked, consumer products totaling $5,000 or more were sold to you for resale, on a buy-sell, a deposit-commission, or other basis. Generally, report any income from your sale of these products on Schedule C (Form 1040).'); + AddFormInstructionLine(PeriodNo, 'NEC', 7, 'Box 3.', 'Reserved for future use.'); + AddFormInstructionLine(PeriodNo, 'NEC', 8, 'Box 4.', 'Shows backup withholding. A payer must backup withhold on certain payments if you did not give your TIN to the payer. See Form W-9, Request for Taxpayer Identification Number and Certification, for information on backup withholding. Include this amount on your income tax return as tax withheld.'); + AddFormInstructionLine(PeriodNo, 'NEC', 9, 'Boxes 5-7.', 'State income tax withheld reporting boxes.'); + AddFormInstructionLine(PeriodNo, 'NEC', 10, 'Future developments.', 'For the latest information about developments related to Form 1099-NEC and its instructions, such as legislation enacted after they were published, go to www.irs.gov/Form1099NEC.'); + AddFormInstructionLine(PeriodNo, 'NEC', 11, 'Free File Program.', 'Go to www.irs.gov/FreeFile to see if you qualify for no-cost online federal tax preparation, e-filing, and direct deposit or payment options.'); + end; + + local procedure AddMiscFormInstructionLines(PeriodNo: Code[20]) + var + IRS1099Form: Record "IRS 1099 Form"; + begin + if not IRS1099Form.Get(PeriodNo, 'MISC') then + exit; + AddFormInstructionLine(PeriodNo, 'MISC', 1, 'Recipient’s taxpayer identification number (TIN).', 'For your protection, this form may show only the last four digits of your TIN (social security number (SSN), individual taxpayer identification number (ITIN), adoption taxpayer identification number (ATIN), or employer identification number (EIN)). However, the issuer has reported your complete TIN to the IRS.'); + AddFormInstructionLine(PeriodNo, 'MISC', 2, 'Account number.', 'May show an account or other unique number the payer assigned to distinguish your account.'); + AddFormInstructionLine(PeriodNo, 'MISC', 3, 'Amounts shown may be subject to self-employment (SE) tax.', 'Individuals should see the Instructions for Schedule SE (Form 1040). Corporations, fiduciaries, or partnerships must report the amounts on the appropriate line of their tax returns.'); + AddFormInstructionLine(PeriodNo, 'MISC', 4, 'Form 1099-MISC incorrect?', 'If this form is incorrect or has been issued in error, contact the payer. If you cannot get this form corrected, attach an explanation to your tax return and report your information correctly.'); + AddFormInstructionLine(PeriodNo, 'MISC', 5, 'Box 1.', 'Report rents from real estate on Schedule E (Form 1040). However, report rents on Schedule C (Form 1040) if you provided significant services to the tenant, sold real estate as a business, or rented personal property as a business. See Pub. 527.'); + AddFormInstructionLine(PeriodNo, 'MISC', 6, 'Box 2.', 'Report royalties from oil, gas, or mineral properties; copyrights; and patents on Schedule E (Form 1040). However, report payments for a working interest as explained in the Schedule E (Form 1040) instructions. For royalties on timber, coal, and iron ore, see Pub. 544.'); + AddFormInstructionLine(PeriodNo, 'MISC', 7, 'Box 3.', 'Generally, report this amount on the “Other income” line of Schedule 1 (Form 1040) and identify the payment. The amount shown may be payments received as the beneficiary of a deceased employee, prizes, awards, taxable damages, Indian gaming profits, or other taxable income. See Pub. 525. If it is trade or business income, report this amount on Schedule C or F (Form 1040).'); + AddFormInstructionLine(PeriodNo, 'MISC', 8, 'Box 4.', 'Shows backup withholding or withholding on Indian gaming profits. Generally, a payer must backup withhold if you did not furnish your TIN. See Form W-9 and Pub. 505 for more information. Report this amount on your income tax return as tax withheld.'); + AddFormInstructionLine(PeriodNo, 'MISC', 9, 'Box 5.', 'Shows the amount paid to you as a fishing boat crew member by the operator, who considers you to be self-employed. Self-employed individuals must report this amount on Schedule C (Form 1040). See Pub. 334.'); + AddFormInstructionLine(PeriodNo, 'MISC', 10, 'Box 6.', 'For individuals, report on Schedule C (Form 1040).'); + AddFormInstructionLine(PeriodNo, 'MISC', 11, 'Box 7.', 'If checked, consumer products totaling $5,000 or more were sold to you for resale, on a buy-sell, a deposit-commission, or other basis. Generally, report any income from your sale of these products on Schedule C (Form 1040).'); + AddFormInstructionLine(PeriodNo, 'MISC', 12, 'Box 8.', 'Shows substitute payments in lieu of dividends or tax-exempt interest received by your broker on your behalf as a result of a loan of your securities. Report on the “Other income” line of Schedule 1 (Form 1040).'); + AddFormInstructionLine(PeriodNo, 'MISC', 13, 'Box 9.', 'Report this amount on Schedule F (Form 1040).'); + AddFormInstructionLine(PeriodNo, 'MISC', 14, 'Box 10.', 'Shows gross proceeds paid to an attorney in connection with legal services. Report only the taxable part as income on your return.'); + AddFormInstructionLine(PeriodNo, 'MISC', 15, 'Box 11.', 'Shows the amount of cash you received for the sale of fish if you are in the trade or business of catching fish.'); + AddFormInstructionLine(PeriodNo, 'MISC', 16, 'Box 12.', 'May show current year deferrals as a nonemployee under a nonqualified deferred compensation (NQDC) plan that is subject to the requirements of section 409A plus any earnings on current and prior year deferrals.'); + AddFormInstructionLine(PeriodNo, 'MISC', 17, 'Box 13.', 'If the FATCA filing requirement box is checked, the payer is reporting on this Form 1099 to satisfy its account reporting requirement under chapter 4 of the Internal Revenue Code. You may also have a filing requirement. See the Instructions for Form 8938.'); + AddFormInstructionLine(PeriodNo, 'MISC', 18, 'Box 14.', 'Shows your total compensation of excess golden parachute payments subject to a 20% excise tax. See your tax return instructions for where to report.'); + AddFormInstructionLine(PeriodNo, 'MISC', 19, 'Box 15.', 'Shows income as a nonemployee under an NQDC plan that does not meet the requirements of section 409A. Any amount included in box 12 that is currently taxable is also included in this box. Report this amount as income on your tax return. This income is also subject to a substantial additional tax to be reported on Form 1040, 1040-SR, or 1040-NR. See the instructions for your tax return.'); + AddFormInstructionLine(PeriodNo, 'MISC', 20, 'Boxes 16-18.', 'Show state or local income tax withheld from the payments.'); + AddFormInstructionLine(PeriodNo, 'MISC', 21, 'Future developments.', 'For the latest information about developments related to Form 1099-MISC and its instructions, such as legislation enacted after they were published, go to www.irs.gov/Form1099MISC.'); + AddFormInstructionLine(PeriodNo, 'MISC', 22, 'Free File Program.', 'Go to www.irs.gov/FreeFile to see if you qualify for no-cost online federal tax preparation, e-filing, and direct deposit or payment options.'); + end; + + local procedure AddForm(PeriodNo: Code[20]; FormNo: Code[20]; Description: Text) + var + IRS1099Form: Record "IRS 1099 Form"; + begin + IRS1099Form.Validate("Period No.", PeriodNo); + IRS1099Form.Validate("No.", FormNo); + IRS1099Form.Validate("Description", Description); + IRS1099Form.Insert(true); + end; + + local procedure AddFormBox(PeriodNo: Code[20]; FormNo: Code[20]; FormBoxNo: Code[20]; Description: Text; MinimumReportableAmount: Decimal) + var + IRS1099FormBox: Record "IRS 1099 Form Box"; + begin + IRS1099FormBox.Validate("Period No.", PeriodNo); + IRS1099FormBox.Validate("Form No.", FormNo); + IRS1099FormBox.Validate("No.", FormBoxNo); + IRS1099FormBox.Validate("Description", Description); + IRS1099FormBox.Validate("Minimum Reportable Amount", MinimumReportableAmount); + IRS1099FormBox.Insert(true); + end; + + local procedure AddFormStatementLine(PeriodNo: Code[20]; FormNo: Code[20]; FormBoxNo: Code[20]; StatementLineNo: Integer; Description: Text) + begin + AddFormStatementLine(PeriodNo, FormNo, Enum::"IRS 1099 Print Value Type"::Amount, FormBoxNo, StatementLineNo, Description); + end; + + local procedure AddFormStatementLine(PeriodNo: Code[20]; FormNo: Code[20]; Type: Enum "IRS 1099 Print Value Type"; FormBoxNo: Code[20]; StatementLineNo: Integer; Description: Text) + var + IRS1099FormStatementLine: Record "IRS 1099 Form Statement Line"; + begin + IRS1099FormStatementLine.Validate("Period No.", PeriodNo); + IRS1099FormStatementLine.Validate("Form No.", FormNo); + IRS1099FormStatementLine.Validate("Line No.", StatementLineNo); + IRS1099FormStatementLine.Validate("Print Value Type", Type); + IRS1099FormStatementLine.Validate("Row No.", FormBoxNo); + IRS1099FormStatementLine.Validate("Description", Description); + IRS1099FormStatementLine.Validate("Filter Expression", StrSubstNo(IRSFormStatementLineFilterExpressionTxt, FormBoxNo)); + IRS1099FormStatementLine.Insert(true); + end; + + local procedure AddFormInstructionLine(PeriodNo: Code[20]; FormNo: Code[20]; LineNo: Integer; Header: Text; Description: Text) + var + IRS1099FormInstruction: Record "IRS 1099 Form Instruction"; + begin + IRS1099FormInstruction.Validate("Period No.", PeriodNo); + IRS1099FormInstruction.Validate("Form No.", FormNo); + IRS1099FormInstruction.Validate("Line No.", LineNo); + IRS1099FormInstruction.Validate(Header, Header); + IRS1099FormInstruction.Validate(Description, Description); + IRS1099FormInstruction.Insert(true); + end; +} \ No newline at end of file diff --git a/Apps/W1/HybridSL/app/src/codeunits/SLPopulateVendor1099Data.Codeunit.al b/Apps/W1/HybridSL/app/src/codeunits/SLPopulateVendor1099Data.Codeunit.al new file mode 100644 index 0000000000..d671e99208 --- /dev/null +++ b/Apps/W1/HybridSL/app/src/codeunits/SLPopulateVendor1099Data.Codeunit.al @@ -0,0 +1,766 @@ +// ------------------------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ------------------------------------------------------------------------------------------------ + +namespace Microsoft.DataMigration.SL; + +using Microsoft.Finance.GeneralLedger.Journal; +using Microsoft.Finance.GeneralLedger.Posting; +using Microsoft.Finance.ReceivablesPayables; +using Microsoft.Purchases.Payables; +using Microsoft.Finance.GeneralLedger.Account; +using Microsoft.Finance.VAT.Reporting; +using Microsoft.Foundation.NoSeries; +using Microsoft.Purchases.Vendor; +using System.Integration; + +codeunit 47007 "SL Populate Vendor 1099 Data" +{ + EventSubscriberInstance = Manual; + Permissions = tabledata "IRS 1099 Form Box" = R, + tabledata "IRS 1099 Vendor Form Box Setup" = RIM; + + var + DefaultPayablesAccountCode: Code[20]; + PostingDate: Date; + NextYearPostingDate: Date; + SLCurr1099Yr: Integer; + SLNext1099Yr: Integer; + Current1099YearOpen: Boolean; + Next1099YearOpen: Boolean; + MessageCodeAbortedTxt: Label 'ABORTED', Locked = true; + MessageCodeCompletedTxt: Label 'COMPLETED', Locked = true; + MessageCodeSkippedTxt: Label 'SKIPPED', Locked = true; + MessageCodeStartTxt: Label 'START', Locked = true; + MessageCodeProcessingTxt: Label 'PROCESSING', Locked = true; + NoSeriesDescriptionTxt: Label 'SL Vendor 1099', Locked = true; + SourceCodeTxt: Label 'GENJNL', Locked = true; + VendorTaxBatchNameTxt: Label 'SL1099', Locked = true; + VendorTaxNoSeriesTxt: Label 'VENDTAX', Locked = true; + + [EventSubscriber(ObjectType::Codeunit, Codeunit::"Gen. Jnl.-Post", 'OnBeforeCode', '', false, false)] + local procedure OnBeforeCode(var GenJournalLine: Record "Gen. Journal Line"; var HideDialog: Boolean) + begin + HideDialog := true; + end; + + [EventSubscriber(ObjectType::Codeunit, Codeunit::"Gen. Jnl.-Post", 'OnBeforeShowPostResultMessage', '', false, false)] + local procedure OnBeforeShowPostResultMessage(var GenJnlLine: Record "Gen. Journal Line"; TempJnlBatchName: Code[10]; var IsHandled: Boolean) + begin + IsHandled := true; + end; + + trigger OnRun() + var + SLCompanyAdditionalSettings: Record "SL Company Additional Settings"; + SLVendor1099MappingHelpers: Codeunit "SL Vendor 1099 Mapping Helpers"; + begin + if not SLCompanyAdditionalSettings.GetMigrateCurrent1099YearEnabled() then + LogMessage(MessageCodeSkippedTxt, 'SL Vendor Current 1099 Year Data Migration is not enabled on the SL Company Migration Configuration page.'); + if not SLCompanyAdditionalSettings.GetMigrateNext1099YearEnabled() then + LogMessage(MessageCodeSkippedTxt, 'SL Vendor Next 1099 Year Data Migration is not enabled on the SL Company Migration Configuration page.'); + if not SLCompanyAdditionalSettings.GetMigrateCurrent1099YearEnabled() and not SLCompanyAdditionalSettings.GetMigrateNext1099YearEnabled() then begin + LogMessage(MessageCodeAbortedTxt, 'SL Vendor 1099 Data Migration is not enabled on the SL Company Migration Configuration page.'); + exit; + end; + + SLCurr1099Yr := SLVendor1099MappingHelpers.GetCurrent1099YearFromSLAPSetup(); + if SLCurr1099Yr <> 0 then begin + Current1099YearOpen := SLVendor1099MappingHelpers.GetCurrent1099YearOpenStatus(); + if not Current1099YearOpen then + LogMessage(MessageCodeSkippedTxt, 'SL Current 1099 Year ' + Format(SLCurr1099Yr) + ' is not open for data entry.'); + end; + + SLNext1099Yr := SLVendor1099MappingHelpers.GetNext1099YearFromSLAPSetup(); + if SLNext1099Yr <> 0 then begin + Next1099YearOpen := SLVendor1099MappingHelpers.GetNext1099YearOpenStatus(); + if not Next1099YearOpen then + LogMessage(MessageCodeSkippedTxt, 'SL Next 1099 Year ' + Format(SLNext1099Yr) + ' is not open for data entry.'); + end; + if not Current1099YearOpen and not Next1099YearOpen then begin + LogMessage(MessageCodeAbortedTxt, 'Neither the SL Current 1099 Year nor the Next 1099 Year is open for data entry.'); + exit; + end; + + UpdateAllVendorTaxInfo(); + end; + + local procedure UpdateAllVendorTaxInfo() + begin + Initialize(); + LogMessage(MessageCodeCompletedTxt, 'Completed Initialization for Vendor Tax Info Update'); + if (SLCurr1099Yr <> 0) and (Current1099YearOpen) then begin + LogMessage(MessageCodeStartTxt, 'Starting SL Vendor 1099 Data Migration for Current 1099 Tax Year ' + Format(SLCurr1099Yr)); + UpdateVendorTaxInfo(SLCurr1099Yr, PostingDate); + LogMessage(MessageCodeCompletedTxt, 'Completed SL Vendor 1099 Data Migration for Current 1099 Tax Year ' + Format(SLCurr1099Yr)); + end; + + if (SLNext1099Yr <> 0) and (Next1099YearOpen) then begin + LogMessage(MessageCodeStartTxt, 'Starting SL Vendor 1099 Data Migration for Next 1099 Tax Year ' + Format(SLNext1099Yr)); + UpdateVendorTaxInfo(SLNext1099Yr, NextYearPostingDate); + LogMessage(MessageCodeCompletedTxt, 'Completed SL Vendor 1099 Data Migration for Next 1099 Tax Year ' + Format(SLNext1099Yr)); + end; + + LogMessage(MessageCodeCompletedTxt, 'Completed Vendor Tax Info Update'); + + CleanUp(); + end; + + local procedure Initialize() + var + SLCompanyAdditionalSettings: Record "SL Company Additional Settings"; + DataMigrationFacadeHelper: Codeunit "Data Migration Facade Helper"; + SLHelperFunctions: Codeunit "SL Helper Functions"; + CurrentYear: Integer; + Day31: Integer; + Month12: Integer; + NextYear: Integer; + VendorTaxBatchCode: Code[10]; + Day31Txt: Label '31', Locked = true; + Month12Txt: Label '12', Locked = true; + begin + SLCompanyAdditionalSettings.GetSingleInstance(); + CurrentYear := SLCurr1099Yr; + NextYear := SLNext1099Yr; + + CreateMappingsIfNeeded(); + + Evaluate(Day31, Day31Txt); + Evaluate(Month12, Month12Txt); + PostingDate := DMY2Date(Day31, Month12, CurrentYear); + NextYearPostingDate := DMY2Date(Day31, Month12, NextYear); + + CreateNoSeriesIfNeeded(); + DefaultPayablesAccountCode := SLHelperFunctions.GetPostingAccountNumber('PayablesAccount'); + + if (CurrentYear <> 0) and (Current1099YearOpen) then begin + VendorTaxBatchCode := VendorTaxBatchNameTxt + Format(CurrentYear); + CreateGeneralJournalBatchIfNeeded(VendorTaxBatchCode, '', '', CurrentYear); + end; + if (NextYear <> 0) and (Next1099YearOpen) then begin + VendorTaxBatchCode := VendorTaxBatchNameTxt + Format(NextYear); + CreateGeneralJournalBatchIfNeeded(VendorTaxBatchCode, '', '', NextYear); + end; + + DataMigrationFacadeHelper.CreateSourceCodeIfNeeded(SourceCodeTxt); + end; + + local procedure CreateMappingsIfNeeded() + begin + Create1099Mappings(SLCurr1099Yr, Current1099YearOpen); + Create1099Mappings(SLNext1099Yr, Next1099YearOpen); + end; + + local procedure Create1099Mappings(TaxYear: Integer; Open1099Year: Boolean) + var + SLSupportedTaxYear: Record "SL Supported Tax Year"; + SLVendor1099MappingHelpers: Codeunit "SL Vendor 1099 Mapping Helpers"; + begin + if TaxYear = 0 then + exit; + if not Open1099Year then + exit; + if SLSupportedTaxYear.Get(TaxYear) then + exit; + SLVendor1099MappingHelpers.InsertSupportedTaxYear(TaxYear); + + // MISC + SLVendor1099MappingHelpers.InsertMapping(TaxYear, '1', '1M', 'MISC', 'MISC-01'); + SLVendor1099MappingHelpers.InsertMapping(TaxYear, '2', '2M', 'MISC', 'MISC-02'); + SLVendor1099MappingHelpers.InsertMapping(TaxYear, '3', '3M', 'MISC', 'MISC-03'); + SLVendor1099MappingHelpers.InsertMapping(TaxYear, '4', '4M', 'MISC', 'MISC-04'); + SLVendor1099MappingHelpers.InsertMapping(TaxYear, '5', '5M', 'MISC', 'MISC-05'); + SLVendor1099MappingHelpers.InsertMapping(TaxYear, '6', '6M', 'MISC', 'MISC-06'); + SLVendor1099MappingHelpers.InsertMapping(TaxYear, '8', '8M', 'MISC', 'MISC-08'); + SLVendor1099MappingHelpers.InsertMapping(TaxYear, '10', '9M', 'MISC', 'MISC-09'); + SLVendor1099MappingHelpers.InsertMapping(TaxYear, '14', '10M', 'MISC', 'MISC-10'); + SLVendor1099MappingHelpers.InsertMapping(TaxYear, '15', '12M', 'MISC', 'MISC-12'); + SLVendor1099MappingHelpers.InsertMapping(TaxYear, '13', '14M', 'MISC', 'MISC-14'); + SLVendor1099MappingHelpers.InsertMapping(TaxYear, '25', '15M', 'MISC', 'MISC-15'); + + // NEC + SLVendor1099MappingHelpers.InsertMapping(TaxYear, '7', '1N', 'NEC', 'NEC-01'); + + LogMessage(MessageCodeCompletedTxt, 'Completed Box Mappings for Tax Year ' + Format(TaxYear)); + end; + + local procedure CreateNoSeriesIfNeeded() + var + NoSeries: Record "No. Series"; + NoSeriesLine: Record "No. Series Line"; + begin + if not NoSeries.Get(VendorTaxNoSeriesTxt) then begin + NoSeries."Code" := VendorTaxNoSeriesTxt; + NoSeries.Description := NoSeriesDescriptionTxt; + NoSeries."Default Nos." := true; + NoSeries."Manual Nos." := true; + NoSeries.Insert(); + + NoSeriesLine."Series Code" := VendorTaxNoSeriesTxt; + NoSeriesLine."Starting No." := 'VT000000'; + NoSeriesLine."Ending No." := 'VT999999'; + NoSeriesLine."Increment-by No." := 1; + NoSeriesLine.Open := true; + NoSeriesLine.Insert(); + end; + end; + + local procedure UpdateVendorTaxInfo(TaxYear: Integer; YearEndPostingDate: Date) + var + SLVendor: Record "SL Vendor"; + begin + SLVendor.SetRange(Vend1099, 1); + if SLVendor.FindSet() then + repeat + LogVendorMessage(CopyStr(SLVendor.VendId, 1, MaxStrLen(SLVendor.VendId)), MessageCodeProcessingTxt, 'Processing Tax Info for Vendor No.: ' + SLVendor.VendId.Trim() + ', ' + 'Tax Year: ' + Format(TaxYear)); + ProcessVendorTaxInfo(SLVendor, TaxYear, YearEndPostingDate); + until SLVendor.Next() = 0; + end; + + local procedure ProcessVendorTaxInfo(var SLVendor: Record "SL Vendor"; TaxYear: Integer; YearEndPostingDate: Date) + var + Vendor: Record Vendor; + SLVendor1099MappingHelpers: Codeunit "SL Vendor 1099 Mapping Helpers"; + IRS1099Code: Code[10]; + begin + if not Vendor.Get(SLVendor.VendId) then begin + LogVendorMessage(SLVendor.VendId, MessageCodeAbortedTxt, 'Vendor No.: ' + SLVendor.VendId.Trim() + ' not found in Business Central.'); + exit; + end; + + IRS1099Code := SLVendor1099MappingHelpers.GetIRS1099BoxCode(TaxYear, SLVendor.DfltBox); + if IRS1099Code <> '' then + if not VendorAlreadyHasIRS1099CodeAssigned(Vendor, TaxYear) then begin + AssignIRS1099CodeToVendor(Vendor, IRS1099Code, TaxYear); + if SLVendor.TIN.TrimEnd() <> '' then + Vendor.Validate("Federal ID No.", SLVendor.TIN.TrimEnd()); + if SLVendor.S4Future09 = 1 then + Vendor.Validate("FATCA Requirement", true); + if SLVendor.TIN.TrimEnd() <> '' then + Vendor.Validate("Tax Identification Type", Vendor."Tax Identification Type"::"Legal Entity"); + if not Vendor.Modify() then begin + LogLastError(Vendor."No."); + exit; + end; + end else + LogVendorSkipped(Vendor."No.", 'Vendor already has an IRS 1099 Code assigned.'); + + AddVendor1099Values(Vendor, TaxYear, YearEndPostingDate); + end; + + local procedure VendorAlreadyHasIRS1099CodeAssigned(var Vendor: Record Vendor; TaxYear: Integer): Boolean + var + SLCompanyAdditionalSettings: Record "SL Company Additional Settings"; + IRS1099VendorFormBoxSetup: Record "IRS 1099 Vendor Form Box Setup"; + begin + SLCompanyAdditionalSettings.GetSingleInstance(); + if IRS1099VendorFormBoxSetup.Get(TaxYear, Vendor."No.") then + exit(true); + end; + + local procedure AssignIRS1099CodeToVendor(var Vendor: Record Vendor; IRS1099Code: Code[10]; TaxYear: Integer): Boolean + var + SLCompanyAdditionalSettings: Record "SL Company Additional Settings"; + IRS1099VendorFormBoxSetup: Record "IRS 1099 Vendor Form Box Setup"; + IRS1099FormBox: Record "IRS 1099 Form Box"; + begin + IRS1099FormBox.SetRange("No.", IRS1099Code); + if not IRS1099FormBox.FindFirst() then + exit(false); + + SLCompanyAdditionalSettings.GetSingleInstance(); + IRS1099VendorFormBoxSetup.Validate("Period No.", Format(TaxYear)); + IRS1099VendorFormBoxSetup.Validate("Vendor No.", Vendor."No."); + IRS1099VendorFormBoxSetup.Validate("Form No.", IRS1099FormBox."Form No."); + IRS1099VendorFormBoxSetup.Validate("Form Box No.", IRS1099Code); + IRS1099VendorFormBoxSetup.Insert(true); + LogVendorDefaultBoxMessage(Vendor."No.", IRS1099Code, MessageCodeCompletedTxt, 'Assigned IRS 1099 Code ' + IRS1099Code + ' to Vendor No.: ' + Vendor."No."); + exit(true); + end; + + local procedure AddVendor1099Values(var Vendor: Record Vendor; TaxYear: Integer; YearEndPostingDate: Date) + var + InvoiceGenJournalLine: Record "Gen. Journal Line"; + PaymentGenJournalLine: Record "Gen. Journal Line"; + SLCompanyAdditionalSettings: Record "SL Company Additional Settings"; + NoSeries: Codeunit "No. Series"; + InvoiceDocumentNo: Code[20]; + InvoiceExternalDocumentNo: Code[35]; + IRS1099Code: Code[10]; + PaymentDocumentNo: Code[20]; + PaymentExternalDocumentNo: Code[35]; + VendorPayablesAccountCode: Code[20]; + VendorYear1099AmountDictionary: Dictionary of [Code[10], Decimal]; + TaxAmount: Decimal; + InvoiceCreated: Boolean; + PaymentCreated: Boolean; + begin + BuildVendor1099Entries(Vendor."No.", VendorYear1099AmountDictionary, TaxYear); + LogVendorMessage(Vendor."No.", MessageCodeCompletedTxt, 'Built 1099 Entries for Vendor No.: ' + Vendor."No." + ' for Tax Year: ' + Format(TaxYear)); + if VendorYear1099AmountDictionary.Count() = 0 then begin + LogVendorSkipped(Vendor."No.", 'No 1099 amounts found for Vendor: ' + Vendor."No." + ' for Tax Year: ' + Format(TaxYear)); + exit; + end; + + VendorPayablesAccountCode := GetPostingAccountNo(Vendor); + if VendorPayablesAccountCode = '' then begin + LogErrorMessage(Vendor."No.", 'No Payables Account found.'); + exit; + end; + + SLCompanyAdditionalSettings.GetSingleInstance(); + foreach IRS1099Code in VendorYear1099AmountDictionary.Keys() do begin + TaxAmount := VendorYear1099AmountDictionary.Get(IRS1099Code); + + if TaxAmount > 0 then begin + // Invoice + InvoiceExternalDocumentNo := CopyStr(IRS1099Code + '-INV-' + Format(TaxYear), 1, MaxStrLen(InvoiceExternalDocumentNo)); + InvoiceDocumentNo := NoSeries.GetNextNo(VendorTaxNoSeriesTxt); + InvoiceCreated := CreateGeneralJournalLine(InvoiceGenJournalLine, + Vendor."No.", + "Gen. Journal Document Type"::Invoice, + InvoiceDocumentNo, + IRS1099Code, + Vendor."No.", + -TaxAmount, + VendorPayablesAccountCode, + IRS1099Code, + InvoiceExternalDocumentNo, + YearEndPostingDate, + TaxYear); + + if InvoiceCreated then + LogVendorDefaultBoxMessage(Vendor."No.", IRS1099Code, MessageCodeCompletedTxt, 'Created Invoice Journal Line for Amount: ' + Format(-TaxAmount)); + + // Payment + PaymentExternalDocumentNo := CopyStr(IRS1099Code + '-PMT-' + Format(TaxYear), 1, MaxStrLen(PaymentExternalDocumentNo)); + PaymentDocumentNo := NoSeries.GetNextNo(VendorTaxNoSeriesTxt); + PaymentCreated := CreateGeneralJournalLine(PaymentGenJournalLine, + Vendor."No.", + "Gen. Journal Document Type"::Payment, + PaymentDocumentNo, + IRS1099Code, + Vendor."No.", + TaxAmount, + VendorPayablesAccountCode, + IRS1099Code, + PaymentExternalDocumentNo, + YearEndPostingDate, + TaxYear); + + if PaymentCreated then + LogVendorDefaultBoxMessage(Vendor."No.", IRS1099Code, MessageCodeCompletedTxt, 'Created Payment Journal Line for Amount: ' + Format(TaxAmount)); + + if InvoiceCreated and PaymentCreated then begin + InvoiceGenJournalLine.SendToPosting(Codeunit::"Gen. Jnl.-Post"); + PaymentGenJournalLine.SendToPosting(Codeunit::"Gen. Jnl.-Post"); + ApplyEntries(Vendor."No.", InvoiceDocumentNo, PaymentDocumentNo, InvoiceExternalDocumentNo, YearEndPostingDate); + end; + end; + end; + end; + + local procedure GetPostingAccountNo(var Vendor: Record Vendor): Code[20] + var + VendorPostingGroup: Record "Vendor Posting Group"; + GLAccount: Record "G/L Account"; + begin + if Vendor."Vendor Posting Group" = '' then + exit(DefaultPayablesAccountCode); + + if VendorPostingGroup.Get(Vendor."Vendor Posting Group") then + if VendorPostingGroup."Payables Account" <> '' then + if GLAccount.Get(VendorPostingGroup."Payables Account") then + exit(GLAccount."No."); + + exit(''); + end; + + local procedure BuildVendor1099Entries(VendorNo: Code[20]; var VendorYear1099AmountDictionary: Dictionary of [Code[10], Decimal]; TaxYear: Integer) + var + SLAPBalances: Record "SL AP_Balances"; + SLVendor1099MappingHelpers: Codeunit "SL Vendor 1099 Mapping Helpers"; + IRS1099Code: Code[10]; + TaxAmount: Decimal; + begin + if SLAPBalances.Get(VendorNo, CompanyName) then begin + if SLAPBalances.CYBox00 > 0 then + IRS1099Code := SLVendor1099MappingHelpers.GetIRS1099BoxCode(TaxYear, '1') + else + IRS1099Code := ''; + if IRS1099Code <> '' then + if VendorYear1099AmountDictionary.Get(IRS1099Code, TaxAmount) then + VendorYear1099AmountDictionary.Set(IRS1099Code, TaxAmount + SLAPBalances.CYBox00) + else + VendorYear1099AmountDictionary.Add(IRS1099Code, SLAPBalances.CYBox00); + if SLAPBalances.CYBox01 > 0 then + IRS1099Code := SLVendor1099MappingHelpers.GetIRS1099BoxCode(TaxYear, '2') + else + IRS1099Code := ''; + if IRS1099Code <> '' then + if VendorYear1099AmountDictionary.Get(IRS1099Code, TaxAmount) then + VendorYear1099AmountDictionary.Set(IRS1099Code, TaxAmount + SLAPBalances.CYBox01) + else + VendorYear1099AmountDictionary.Add(IRS1099Code, SLAPBalances.CYBox01); + if SLAPBalances.CYBox02 > 0 then + IRS1099Code := SLVendor1099MappingHelpers.GetIRS1099BoxCode(TaxYear, '3') + else + IRS1099Code := ''; + if IRS1099Code <> '' then + if VendorYear1099AmountDictionary.Get(IRS1099Code, TaxAmount) then + VendorYear1099AmountDictionary.Set(IRS1099Code, TaxAmount + SLAPBalances.CYBox02) + else + VendorYear1099AmountDictionary.Add(IRS1099Code, SLAPBalances.CYBox02); + if SLAPBalances.CYBox03 > 0 then + IRS1099Code := SLVendor1099MappingHelpers.GetIRS1099BoxCode(TaxYear, '4') + else + IRS1099Code := ''; + if IRS1099Code <> '' then + if VendorYear1099AmountDictionary.Get(IRS1099Code, TaxAmount) then + VendorYear1099AmountDictionary.Set(IRS1099Code, TaxAmount + SLAPBalances.CYBox03) + else + VendorYear1099AmountDictionary.Add(IRS1099Code, SLAPBalances.CYBox03); + if SLAPBalances.CYBox04 > 0 then + IRS1099Code := SLVendor1099MappingHelpers.GetIRS1099BoxCode(TaxYear, '5') + else + IRS1099Code := ''; + if IRS1099Code <> '' then + if VendorYear1099AmountDictionary.Get(IRS1099Code, TaxAmount) then + VendorYear1099AmountDictionary.Set(IRS1099Code, TaxAmount + SLAPBalances.CYBox04) + else + VendorYear1099AmountDictionary.Add(IRS1099Code, SLAPBalances.CYBox04); + if SLAPBalances.CYBox05 > 0 then + IRS1099Code := SLVendor1099MappingHelpers.GetIRS1099BoxCode(TaxYear, '6') + else + IRS1099Code := ''; + if IRS1099Code <> '' then + if VendorYear1099AmountDictionary.Get(IRS1099Code, TaxAmount) then + VendorYear1099AmountDictionary.Set(IRS1099Code, TaxAmount + SLAPBalances.CYBox05) + else + VendorYear1099AmountDictionary.Add(IRS1099Code, SLAPBalances.CYBox05); + if SLAPBalances.CYBox06 > 0 then + IRS1099Code := SLVendor1099MappingHelpers.GetIRS1099BoxCode(TaxYear, '7') + else + IRS1099Code := ''; + if IRS1099Code <> '' then + if VendorYear1099AmountDictionary.Get(IRS1099Code, TaxAmount) then + VendorYear1099AmountDictionary.Set(IRS1099Code, TaxAmount + SLAPBalances.CYBox06) + else + VendorYear1099AmountDictionary.Add(IRS1099Code, SLAPBalances.CYBox06); + if SLAPBalances.CYBox07 > 0 then + IRS1099Code := SLVendor1099MappingHelpers.GetIRS1099BoxCode(TaxYear, '8') + else + IRS1099Code := ''; + if IRS1099Code <> '' then + if VendorYear1099AmountDictionary.Get(IRS1099Code, TaxAmount) then + VendorYear1099AmountDictionary.Set(IRS1099Code, TaxAmount + SLAPBalances.CYBox07) + else + VendorYear1099AmountDictionary.Add(IRS1099Code, SLAPBalances.CYBox07); + if SLAPBalances.CYBox09 > 0 then + IRS1099Code := SLVendor1099MappingHelpers.GetIRS1099BoxCode(TaxYear, '10') + else + IRS1099Code := ''; + if IRS1099Code <> '' then + if VendorYear1099AmountDictionary.Get(IRS1099Code, TaxAmount) then + VendorYear1099AmountDictionary.Set(IRS1099Code, TaxAmount + SLAPBalances.CYBox09) + else + VendorYear1099AmountDictionary.Add(IRS1099Code, SLAPBalances.CYBox09); + if SLAPBalances.CYBox14 > 0 then + IRS1099Code := SLVendor1099MappingHelpers.GetIRS1099BoxCode(TaxYear, '14') + else + IRS1099Code := ''; + if IRS1099Code <> '' then + if VendorYear1099AmountDictionary.Get(IRS1099Code, TaxAmount) then + VendorYear1099AmountDictionary.Set(IRS1099Code, TaxAmount + SLAPBalances.CYBox14) + else + VendorYear1099AmountDictionary.Add(IRS1099Code, SLAPBalances.CYBox14); + if SLAPBalances.CYBox11 > 0 then + IRS1099Code := SLVendor1099MappingHelpers.GetIRS1099BoxCode(TaxYear, '15') + else + IRS1099Code := ''; + if IRS1099Code <> '' then + if VendorYear1099AmountDictionary.Get(IRS1099Code, TaxAmount) then + VendorYear1099AmountDictionary.Set(IRS1099Code, TaxAmount + SLAPBalances.CYBox11) + else + VendorYear1099AmountDictionary.Add(IRS1099Code, SLAPBalances.CYBox11); + if SLAPBalances.CYBox13 > 0 then + IRS1099Code := SLVendor1099MappingHelpers.GetIRS1099BoxCode(TaxYear, '13') + else + IRS1099Code := ''; + if IRS1099Code <> '' then + if VendorYear1099AmountDictionary.Get(IRS1099Code, TaxAmount) then + VendorYear1099AmountDictionary.Set(IRS1099Code, TaxAmount + SLAPBalances.CYBox13) + else + VendorYear1099AmountDictionary.Add(IRS1099Code, SLAPBalances.CYBox13); + if SLAPBalances.CYBox12 > 0 then + IRS1099Code := SLVendor1099MappingHelpers.GetIRS1099BoxCode(TaxYear, '25') + else + IRS1099Code := ''; + if IRS1099Code <> '' then + if VendorYear1099AmountDictionary.Get(IRS1099Code, TaxAmount) then + VendorYear1099AmountDictionary.Set(IRS1099Code, TaxAmount + SLAPBalances.CYBox12) + else + VendorYear1099AmountDictionary.Add(IRS1099Code, SLAPBalances.CYBox12); + end; + end; + + local procedure CreateGeneralJournalLine(var GenJournalLine: Record "Gen. Journal Line"; VendorNo: Code[20]; DocumentType: enum "Gen. Journal Document Type"; DocumentNo: Code[20]; + Description: Text[50]; AccountNo: Code[20]; Amount: Decimal; BalancingAccount: Code[20]; IRS1099Code: Code[10]; ExternalDocumentNo: Code[35]; YearEndPostingDate: Date; TaxYear: Integer): boolean + var + GenJournalBatch: Record "Gen. Journal Batch"; + GenJournalLineCurrent: Record "Gen. Journal Line"; + GenJournalTemplate: Record "Gen. Journal Template"; + IRS1099FormBox: Record "IRS 1099 Form Box"; + LineNum: Integer; + VendorTaxBatchCode: Code[10]; + begin + VendorTaxBatchCode := VendorTaxBatchNameTxt + Format(TaxYear); + GenJournalBatch.Get(CreateGenJournalTemplateIfNeeded(VendorTaxBatchCode, TaxYear), VendorTaxBatchCode); + + + GenJournalLineCurrent.SetRange("Journal Template Name", GenJournalBatch."Journal Template Name"); + GenJournalLineCurrent.SetRange("Journal Batch Name", GenJournalBatch.Name); + if GenJournalLineCurrent.FindLast() then + LineNum := GenJournalLineCurrent."Line No." + 10000 + else + LineNum := 10000; + + GenJournalTemplate.Get(GenJournalBatch."Journal Template Name"); + + Clear(GenJournalLine); + GenJournalLine.SetHideValidation(true); + GenJournalLine.Validate("Source Code", GenJournalTemplate."Source Code"); + GenJournalLine.Validate("Journal Template Name", GenJournalBatch."Journal Template Name"); + GenJournalLine.Validate("Journal Batch Name", GenJournalBatch.Name); + GenJournalLine.Validate("Line No.", LineNum); + GenJournalLine.Validate("Account Type", "Gen. Journal Account Type"::Vendor); + GenJournalLine.Validate("Document No.", DocumentNo); + GenJournalLine.Validate("Account No.", AccountNo); + GenJournalLine.Validate(Description, Description); + GenJournalLine.Validate("Document Date", YearEndPostingDate); + GenJournalLine.Validate("Posting Date", YearEndPostingDate); + GenJournalLine.Validate("Due Date", YearEndPostingDate); + GenJournalLine.Validate(Amount, Amount); + GenJournalLine.Validate("Amount (LCY)", Amount); + GenJournalLine.Validate("Currency Code", ''); + GenJournalLine.Validate("Bal. Account Type", GenJournalLine."Bal. Account Type"::"G/L Account"); + GenJournalLine.Validate("Bal. Account No.", BalancingAccount); + GenJournalLine.Validate("Bal. Gen. Posting Type", GenJournalLine."Bal. Gen. Posting Type"::" "); + GenJournalLine.Validate("Bal. Gen. Bus. Posting Group", ''); + GenJournalLine.Validate("Bal. Gen. Prod. Posting Group", ''); + GenJournalLine.Validate("Bal. VAT Prod. Posting Group", ''); + GenJournalLine.Validate("Bal. VAT Bus. Posting Group", ''); + GenJournalLine.Validate("Document Type", DocumentType); + GenJournalLine.Validate("Source Code", SourceCodeTxt); + GenJournalLine.Validate("External Document No.", ExternalDocumentNo); + GenJournalLine.Validate("IRS 1099 Reporting Period", Format(TaxYear)); + + IRS1099FormBox.SetRange("No.", IRS1099Code); + if IRS1099FormBox.FindFirst() then + GenJournalLine.Validate("IRS 1099 Form No.", IRS1099FormBox."Form No."); + + GenJournalLine.Validate("IRS 1099 Form Box No.", IRS1099Code); + GenJournalLine.Validate("IRS 1099 Reporting Amount", Amount); + + if GenJournalLine.Insert(true) then + exit(true) + else + LogLastError(VendorNo); + + exit(false); + end; + + local procedure CreateGenJournalTemplateIfNeeded(GenJournalBatchCode: Code[10]; TaxYear: Integer): Code[10] + var + GenJournalTemplate: Record "Gen. Journal Template"; + begin + GenJournalTemplate.SetRange(Type, GenJournalTemplate.Type::General); + GenJournalTemplate.SetRange(Recurring, false); + if not GenJournalTemplate.FindFirst() then begin + Clear(GenJournalTemplate); + GenJournalTemplate.Validate(Name, GenJournalBatchCode); + GenJournalTemplate.Validate(Type, GenJournalTemplate.Type::General); + GenJournalTemplate.Validate(Recurring, false); + GenJournalTemplate.Validate(Description, GenJournalBatchCode + '-' + 'SL Vendor 1099 Tax Journal for ' + Format(TaxYear)); + GenJournalTemplate.Insert(true); + end; + exit(GenJournalTemplate.Name); + end; + + local procedure ApplyEntries(VendorNo: Code[20]; InvoiceDocumentNo: Code[20]; PaymentDocumentNo: Code[20]; ExternalDocumentNo: Code[35]; YearEndPostingDate: Date) + var + PaymentVendorLedgerEntry: Record "Vendor Ledger Entry"; + InvoiceVendorLedgerEntry: Record "Vendor Ledger Entry"; + ApplyUnapplyParameters: Record "Apply Unapply Parameters"; + VendEntrySetApplID: Codeunit "Vend. Entry-SetAppl.ID"; + VendEntryApplyPostedEntries: Codeunit "VendEntry-Apply Posted Entries"; + begin + PaymentVendorLedgerEntry.SetRange("Vendor No.", VendorNo); + PaymentVendorLedgerEntry.SetRange("Document Type", "Gen. Journal Document Type"::Payment); + PaymentVendorLedgerEntry.SetRange("Document No.", PaymentDocumentNo); + PaymentVendorLedgerEntry.SetRange("Posting Date", YearEndPostingDate); + if PaymentVendorLedgerEntry.FindFirst() then begin + InvoiceVendorLedgerEntry.SetRange("Vendor No.", VendorNo); + InvoiceVendorLedgerEntry.SetRange("Document Type", "Gen. Journal Document Type"::Invoice); + InvoiceVendorLedgerEntry.SetRange("Document No.", InvoiceDocumentNo); + InvoiceVendorLedgerEntry.SetRange("Posting Date", YearEndPostingDate); + + if InvoiceVendorLedgerEntry.FindFirst() then begin + PaymentVendorLedgerEntry.CalcFields(Amount); + InvoiceVendorLedgerEntry.CalcFields(Amount); + + InvoiceVendorLedgerEntry.Validate("Applying Entry", true); + InvoiceVendorLedgerEntry.Validate("Applies-to ID", PaymentVendorLedgerEntry."Document No."); + InvoiceVendorLedgerEntry.CalcFields("Remaining Amount"); + InvoiceVendorLedgerEntry.Validate("Amount to Apply", InvoiceVendorLedgerEntry.Amount); + Codeunit.Run(Codeunit::"Vend. Entry-Edit", InvoiceVendorLedgerEntry); + + VendEntrySetApplID.SetApplId(PaymentVendorLedgerEntry, InvoiceVendorLedgerEntry, PaymentVendorLedgerEntry."Document No."); + + ApplyUnapplyParameters."Account Type" := "Gen. Journal Account Type"::Vendor; + ApplyUnapplyParameters."Account No." := VendorNo; + ApplyUnapplyParameters."Document Type" := InvoiceVendorLedgerEntry."Document Type"; + ApplyUnapplyParameters."Document No." := InvoiceVendorLedgerEntry."Document No."; + ApplyUnapplyParameters."Posting Date" := YearEndPostingDate; + ApplyUnapplyParameters."External Document No." := ExternalDocumentNo; + VendEntryApplyPostedEntries.Apply(InvoiceVendorLedgerEntry, ApplyUnapplyParameters); + end; + end; + end; + + local procedure CreateGeneralJournalBatchIfNeeded(GeneralJournalBatchCode: Code[10]; NoSeriesCode: Code[20]; PostingNoSeriesCode: Code[20]; TaxYear: Integer) + var + GenJournalBatch: Record "Gen. Journal Batch"; + TemplateName: Code[10]; + GenJournalBatchDescriptionTxt: Label 'SL Vendor 1099 Tax Journal for ', Locked = true; + begin + TemplateName := CreateGenJournalTemplateIfNeeded(GeneralJournalBatchCode, TaxYear); + GenJournalBatch.SetRange("Journal Template Name", TemplateName); + GenJournalBatch.SetRange(Name, GeneralJournalBatchCode); + GenJournalBatch.SetRange("No. Series", NoSeriesCode); + GenJournalBatch.SetRange("Posting No. Series", PostingNoSeriesCode); + if not GenJournalBatch.FindFirst() then begin + GenJournalBatch.Init(); + GenJournalBatch.Validate("Journal Template Name", TemplateName); + GenJournalBatch.SetupNewBatch(); + GenJournalBatch.Validate(Name, GeneralJournalBatchCode); + GenJournalBatch.Validate(Description, GenJournalBatchDescriptionTxt + Format(TaxYear)); + GenJournalBatch."No. Series" := NoSeriesCode; + GenJournalBatch."Posting No. Series" := PostingNoSeriesCode; + GenJournalBatch.Insert(true); + end; + end; + + local procedure CleanUp() + var + GenJournalTemplate: Record "Gen. Journal Template"; + GenJournalBatch: Record "Gen. Journal Batch"; + GenJournalLine: Record "Gen. Journal Line"; + CurrentYearJournalBatchName: Code[20]; + NextYearJournalBatchName: Code[20]; + begin + // Current Year + CurrentYearJournalBatchName := VendorTaxBatchNameTxt + Format(SLCurr1099Yr); + GenJournalLine.SetRange("Journal Batch Name", CurrentYearJournalBatchName); + GenJournalLine.SetFilter("Account No.", '<>%1', ''); + if (not GenJournalLine.IsEmpty()) then + exit; + + Clear(GenJournalLine); + GenJournalLine.SetRange("Journal Batch Name", CurrentYearJournalBatchName); + GenJournalLine.SetRange("Account No.", ''); + if not GenJournalLine.IsEmpty() then + GenJournalLine.DeleteAll(); + + GenJournalBatch.SetRange(Name, CurrentYearJournalBatchName); + if GenJournalBatch.FindFirst() then begin + GenJournalBatch.Delete(); + + if (GenJournalTemplate.Get(CurrentYearJournalBatchName)) then + GenJournalTemplate.Delete(); + end; + + // Next Year + NextYearJournalBatchName := VendorTaxBatchNameTxt + Format(SLNext1099Yr); + GenJournalLine.SetRange("Journal Batch Name", NextYearJournalBatchName); + GenJournalLine.SetFilter("Account No.", '<>%1', ''); + if (not GenJournalLine.IsEmpty()) then + exit; + Clear(GenJournalLine); + GenJournalLine.SetRange("Journal Batch Name", NextYearJournalBatchName); + GenJournalLine.SetRange("Account No.", ''); + if not GenJournalLine.IsEmpty() then + GenJournalLine.DeleteAll(); + GenJournalBatch.SetRange(Name, NextYearJournalBatchName); + if GenJournalBatch.FindFirst() then begin + GenJournalBatch.Delete(); + + if (GenJournalTemplate.Get(NextYearJournalBatchName)) then + GenJournalTemplate.Delete(); + end; + end; + + local procedure LogLastError(VendorNo: Code[20]) + var + SL1099MigrationLog: Record "SL 1099 Migration Log"; + begin + SL1099MigrationLog."Vendor No." := VendorNo; + SL1099MigrationLog.IsError := true; + SL1099MigrationLog."Error Code" := CopyStr(GetLastErrorCode(), 1, MaxStrLen(SL1099MigrationLog."Error Code")); + SL1099MigrationLog.SetErrorMessage(GetLastErrorCallStack()); + SL1099MigrationLog.Insert(); + ClearLastError(); + end; + + local procedure LogErrorMessage(VendorNo: Code[20]; ErrorMsg: Text) + var + SL1099MigrationLog: Record "SL 1099 Migration Log"; + begin + SL1099MigrationLog."Vendor No." := VendorNo; + SL1099MigrationLog.IsError := true; + SL1099MigrationLog.SetErrorMessage(ErrorMsg); + SL1099MigrationLog.Insert(); + ClearLastError(); + end; + + local procedure LogVendorSkipped(VendorNo: Code[20]; MessageText: Text[250]) + var + SL1099MigrationLog: Record "SL 1099 Migration Log"; + begin + SL1099MigrationLog."Vendor No." := VendorNo; + SL1099MigrationLog.WasSkipped := true; + SL1099MigrationLog."Message Text" := MessageText; + SL1099MigrationLog.Insert(); + end; + + local procedure LogMessage(MessageCode: Text[100]; MessageText: Text[250]) + var + SL1099MigrationLog: Record "SL 1099 Migration Log"; + begin + SL1099MigrationLog."Message Code" := MessageCode; + SL1099MigrationLog."Message Text" := MessageText; + SL1099MigrationLog.Insert(); + end; + + local procedure LogVendorMessage(VendorNo: Code[20]; MessageCode: Text[100]; MessageText: Text[250]) + var + SL1099MigrationLog: Record "SL 1099 Migration Log"; + begin + SL1099MigrationLog."Vendor No." := VendorNo; + SL1099MigrationLog."Message Code" := MessageCode; + SL1099MigrationLog."Message Text" := MessageText; + SL1099MigrationLog.Insert(); + end; + + local procedure LogVendorDefaultBoxMessage(VendorNo: Code[20]; IRS1099Code: Code[10]; MessageCode: Text[100]; MessageText: Text[250]) + var + SL1099MigrationLog: Record "SL 1099 Migration Log"; + begin + SL1099MigrationLog."Vendor No." := VendorNo; + SL1099MigrationLog."BC IRS 1099 Code" := IRS1099Code; + SL1099MigrationLog."Message Code" := MessageCode; + SL1099MigrationLog."Message Text" := MessageText; + SL1099MigrationLog.Insert(); + end; +} \ No newline at end of file diff --git a/Apps/W1/HybridSL/app/src/codeunits/SLVendor1099MappingHelpers.Codeunit.al b/Apps/W1/HybridSL/app/src/codeunits/SLVendor1099MappingHelpers.Codeunit.al new file mode 100644 index 0000000000..7e7eaa096a --- /dev/null +++ b/Apps/W1/HybridSL/app/src/codeunits/SLVendor1099MappingHelpers.Codeunit.al @@ -0,0 +1,101 @@ +// ------------------------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ------------------------------------------------------------------------------------------------ + +namespace Microsoft.DataMigration.SL; + +codeunit 47008 "SL Vendor 1099 Mapping Helpers" +{ + var + APStringTxt: Label 'AP', Locked = true; + StatusOpenTxt: Label 'O', Locked = true; + + procedure InsertSupportedTaxYear(TaxYear: Integer) + var + SLSupportedTaxYear: Record "SL Supported Tax Year"; + begin + SLSupportedTaxYear."Tax Year" := TaxYear; + SLSupportedTaxYear.Insert(); + end; + + procedure InsertMapping(TaxYear: Integer; SL1099DataValue: Text[2]; SL1099BoxNo: Text[3]; FormType: Text[4]; BCIRS1099Code: Code[10]) + var + SL1099BoxMapping: Record "SL 1099 Box Mapping"; + begin + SL1099BoxMapping."Tax Year" := TaxYear; + SL1099BoxMapping."SL 1099 Box No." := SL1099BoxNo; + SL1099BoxMapping."SL Data Value" := SL1099DataValue; + SL1099BoxMapping."Form Type" := FormType; + SL1099BoxMapping."BC IRS 1099 Code" := BCIRS1099Code; + SL1099BoxMapping.Insert(); + end; + + procedure GetIRS1099BoxCode(TaxYear: Integer; SL1099DataValue: Text[2]): Code[10] + var + SL1099BoxMapping: Record "SL 1099 Box Mapping"; + begin + if SL1099BoxMapping.Get(TaxYear, SL1099DataValue) then + exit(SL1099BoxMapping."BC IRS 1099 Code"); + + exit(''); + end; + + internal procedure GetCurrent1099YearFromSLAPSetup(): Integer + var + SLAPSetup: Record "SL APSetup"; + ReportingYear: Integer; + begin + if not SLAPSetup.Get(APStringTxt) then + exit(0); + + if Evaluate(ReportingYear, SLAPSetup.Curr1099Yr) then + exit(ReportingYear) + else + exit(0); + end; + + internal procedure GetCurrent1099YearOpenStatus(): Boolean + var + SLAPSetup: Record "SL APSetup"; + CurrentYearStatus: Text[1]; + begin + if not SLAPSetup.Get(APStringTxt) then + exit(false); + + CurrentYearStatus := SLAPSetup.CY1099Stat; + if CurrentYearStatus = StatusOpenTxt then + exit(true) + else + exit(false); + end; + + internal procedure GetNext1099YearFromSLAPSetup(): Integer + var + SLAPSetup: Record "SL APSetup"; + ReportingYear: Integer; + begin + if not SLAPSetup.Get(APStringTxt) then + exit(0); + + if Evaluate(ReportingYear, SLAPSetup.Next1099Yr) then + exit(ReportingYear) + else + exit(0); + end; + + internal procedure GetNext1099YearOpenStatus(): Boolean + var + SLAPSetup: Record "SL APSetup"; + NextYearStatus: Text[1]; + begin + if not SLAPSetup.Get(APStringTxt) then + exit(false); + + NextYearStatus := SLAPSetup.NY1099Stat; + if NextYearStatus = StatusOpenTxt then + exit(true) + else + exit(false); + end; +} \ No newline at end of file diff --git a/Apps/W1/HybridSL/app/src/pages/SL1099MigrationLog.Page.al b/Apps/W1/HybridSL/app/src/pages/SL1099MigrationLog.Page.al new file mode 100644 index 0000000000..e627c76fe2 --- /dev/null +++ b/Apps/W1/HybridSL/app/src/pages/SL1099MigrationLog.Page.al @@ -0,0 +1,88 @@ +// ------------------------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ------------------------------------------------------------------------------------------------ + +namespace Microsoft.DataMigration.SL; + +page 47000 "SL 1099 Migration Log" +{ + ApplicationArea = All; + Caption = 'SL 1099 Migration Log'; + PageType = List; + SourceTable = "SL 1099 Migration Log"; + UsageCategory = Administration; + DeleteAllowed = false; + InsertAllowed = false; + ModifyAllowed = false; + + layout + { + area(Content) + { + repeater(General) + { + field("Vendor No."; Rec."Vendor No.") + { + Caption = 'Vendor No.'; + ToolTip = 'Specifies the value of the Vendor No. field.'; + } + field("SL Data Value"; Rec."SL Data Value") + { + Caption = 'SL Data Value'; + ToolTip = 'Specifies the value of the DfltBox field in the SL Vendor table.'; + } + field("SL 1099 Box No."; Rec."SL 1099 Box No.") + { + Caption = 'SL Default 1099 Box No.'; + ToolTip = 'Specifies the SL Default 1099 Box No. based on the SL Data Value field.'; + } + field("Form Type"; Rec."Form Type") + { + Caption = 'Form Type'; + ToolTip = 'Specifies the value of the Form Type field. Value can be MISC or NEC.'; + } + field("BC IRS 1099 Code"; Rec."BC IRS 1099 Code") + { + Caption = 'IRS 1099 Code'; + ToolTip = 'Specifies the value of the IRS 1099 Code in Business Central based on the SL 1099 Box No. field.'; + } + field(WasSkipped; Rec.WasSkipped) + { + Caption = 'Was Skipped'; + ToolTip = 'Specifies whether the record was skipped.'; + } + field(IsError; Rec.IsError) + { + Caption = 'Is Error'; + ToolTip = 'Specifies whether the record has an error.'; + } + field("Error Code"; Rec."Error Code") + { + Caption = 'Error Code'; + ToolTip = 'Specifies the value of the Error Code field.'; + } + field("Error Message"; Rec.GetErrorMessage()) + { + Caption = 'Error Message'; + ToolTip = 'Specifies the value of the Error Message field.'; + } + field("Message Code"; Rec."Message Code") + { + Caption = 'Message Code'; + ToolTip = 'Specifies the value of the Message Code field.'; + } + field("Message Text"; Rec."Message Text") + { + Caption = 'Message Text'; + ToolTip = 'Specifies the value of the Message Text field.'; + } + } + } + } + + procedure FilterOnErrors() + begin + Rec.SetRange(IsError, true); + end; +} \ No newline at end of file diff --git a/Apps/W1/HybridSL/app/src/pages/SLCompanyAddSettingsList.Page.al b/Apps/W1/HybridSL/app/src/pages/SLCompanyAddSettingsList.Page.al index f9cd6ff58f..d1517e985f 100644 --- a/Apps/W1/HybridSL/app/src/pages/SLCompanyAddSettingsList.Page.al +++ b/Apps/W1/HybridSL/app/src/pages/SLCompanyAddSettingsList.Page.al @@ -44,6 +44,16 @@ page 47017 "SL Company Add. Settings List" field("Migrate Payables Module"; Rec."Migrate Payables Module") { } + field("Migrate Current 1099 Year"; Rec."Migrate Current 1099 Year") + { + Caption = 'Migrate Current 1099 Year'; + ToolTip = 'Specifies whether to migrate current 1099 year information from Dynamics SL to Business Central. This option is only available if the Payables module is selected for migration.'; + } + field("Migrate Next 1099 Year"; Rec."Migrate Next 1099 Year") + { + Caption = 'Migrate Next 1099 Year'; + ToolTip = 'Specifies whether to migrate next 1099 year information from Dynamics SL to Business Central. This option is only available if the Payables module is selected for migration.'; + } field("Migrate Open POs"; Rec."Migrate Open POs") { Enabled = false; diff --git a/Apps/W1/HybridSL/app/src/pages/SLMigrationConfiguration.Page.al b/Apps/W1/HybridSL/app/src/pages/SLMigrationConfiguration.Page.al index e9f287c8b5..4bfd5f8e7a 100644 --- a/Apps/W1/HybridSL/app/src/pages/SLMigrationConfiguration.Page.al +++ b/Apps/W1/HybridSL/app/src/pages/SLMigrationConfiguration.Page.al @@ -499,6 +499,44 @@ page 47018 "SL Migration Configuration" } } + group(Vendor1099) + { + Caption = 'Vendor 1099'; + InstructionalText = 'Choose whether to migrate vendor 1099 information from Dynamics SL to Business Central. This option is only available if the Payables module is selected for migration.'; + + field("Migrate Current 1099 Year"; Rec."Migrate Current 1099 Year") + { + Caption = 'Migrate Current 1099 Year'; + ToolTip = 'Specifies whether to migrate current 1099 year information.'; + Enabled = Rec."Migrate Payables Module"; + + trigger OnValidate() + begin + if PrepSettingsForFieldUpdate() then + repeat + SLCompanyAdditionalSettings.Validate("Migrate Current 1099 Year", Rec."Migrate Current 1099 Year"); + SLCompanyAdditionalSettings.Modify(); + until SLCompanyAdditionalSettings.Next() = 0; + end; + } + field("Migrate Next 1099 Year"; Rec."Migrate Next 1099 Year") + { + Caption = 'Migrate Next 1099 Year'; + ToolTip = 'Specifies whether to migrate next 1099 year information.'; + Enabled = Rec."Migrate Payables Module"; + + trigger OnValidate() + begin + if PrepSettingsForFieldUpdate() then + repeat + SLCompanyAdditionalSettings.Validate("Migrate Next 1099 Year", Rec."Migrate Next 1099 Year"); + SLCompanyAdditionalSettings.Modify(); + until SLCompanyAdditionalSettings.Next() = 0; + end; + } + + } + group(HistoricalData) { Caption = 'Historical Snapshot'; diff --git a/Apps/W1/HybridSL/app/src/tables/SL1099BoxMapping.Table.al b/Apps/W1/HybridSL/app/src/tables/SL1099BoxMapping.Table.al new file mode 100644 index 0000000000..d5c20b947b --- /dev/null +++ b/Apps/W1/HybridSL/app/src/tables/SL1099BoxMapping.Table.al @@ -0,0 +1,50 @@ +// ------------------------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ------------------------------------------------------------------------------------------------ + +namespace Microsoft.DataMigration.SL; + +table 47091 "SL 1099 Box Mapping" +{ + Access = Internal; + DataPerCompany = false; + Caption = 'SL 1099 Box Mapping'; + DataClassification = SystemMetadata; + + Fields + { + field(1; "Tax Year"; Integer) + { + Caption = 'Tax Year'; + NotBlank = true; + TableRelation = "SL Supported Tax Year"."Tax Year"; + } + field(2; "SL Data Value"; Text[2]) + { + Caption = 'SL Data Value'; + } + field(3; "SL 1099 Box No."; Text[3]) + { + Caption = 'SL 1099 Box No.'; + NotBlank = true; + } + field(4; "Form Type"; Text[4]) + { + Caption = 'Form Type'; + NotBlank = true; + } + field(5; "BC IRS 1099 Code"; Code[10]) + { + Caption = 'BC IRS 1099 Code'; + NotBlank = true; + } + } + Keys + { + key(Key1; "Tax Year", "SL Data Value") + { + Clustered = true; + } + } +} \ No newline at end of file diff --git a/Apps/W1/HybridSL/app/src/tables/SL1099MigrationLog.Table.al b/Apps/W1/HybridSL/app/src/tables/SL1099MigrationLog.Table.al new file mode 100644 index 0000000000..4f25f70c75 --- /dev/null +++ b/Apps/W1/HybridSL/app/src/tables/SL1099MigrationLog.Table.al @@ -0,0 +1,97 @@ +// ------------------------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ------------------------------------------------------------------------------------------------ + +namespace Microsoft.DataMigration.SL; + +table 47092 "SL 1099 Migration Log" +{ + Access = Internal; + Caption = 'SL 1099 Migration Log'; + DataClassification = SystemMetadata; + + fields + { + field(1; "Primary Key"; Integer) + { + Caption = 'Primary Key'; + AutoIncrement = true; + } + field(2; "Vendor No."; Code[20]) + { + Caption = 'Vendor No.'; + DataClassification = CustomerContent; + } + field(3; "SL Data Value"; Text[2]) + { + Caption = 'SL Data Value'; + } + field(4; "SL 1099 Box No."; Text[3]) + { + Caption = 'SL 1099 Box No.'; + } + field(5; "Form Type"; Text[4]) + { + Caption = 'Form Type'; + } + field(6; "BC IRS 1099 Code"; Code[10]) + { + Caption = 'BC IRS 1099 Code'; + } + field(7; IsError; Boolean) + { + Caption = 'Error'; + } + field(8; WasSkipped; Boolean) + { + Caption = 'Skipped'; + } + field(9; "Error Code"; Text[100]) + { + Caption = 'Error Code'; + } + field(10; "Error Message"; Blob) + { + Caption = 'Error Message'; + } + field(11; "Message Code"; Text[100]) + { + Caption = 'Message Code'; + } + field(12; "Message Text"; Text[250]) + { + Caption = 'Message Text'; + } + } + + keys + { + key(PK; "Primary Key") + { + Clustered = true; + } + } + + procedure SetErrorMessage(ErrorMessageText: Text) + var + ErrorMessageOutStream: OutStream; + begin + Rec."Error Message".CreateOutStream(ErrorMessageOutStream); + ErrorMessageOutStream.WriteText(ErrorMessageText); + end; + + procedure GetErrorMessage(): Text + var + ErrorMessageInStream: InStream; + ReturnText: Text; + begin + CalcFields(Rec."Error Message"); + if Rec."Error Message".HasValue() then begin + Rec."Error Message".CreateInStream(ErrorMessageInStream); + ErrorMessageInStream.ReadText(ReturnText); + end; + + exit(ReturnText) + end; +} \ No newline at end of file diff --git a/Apps/W1/HybridSL/app/src/tables/SLCompanyAdditionalSettings.Table.al b/Apps/W1/HybridSL/app/src/tables/SLCompanyAdditionalSettings.Table.al index fb96149f7c..e0df981a36 100644 --- a/Apps/W1/HybridSL/app/src/tables/SLCompanyAdditionalSettings.Table.al +++ b/Apps/W1/HybridSL/app/src/tables/SLCompanyAdditionalSettings.Table.al @@ -140,6 +140,8 @@ table 47061 "SL Company Additional Settings" Rec.Validate("Migrate Vendor Classes", false); Rec.Validate("Migrate Only Payables Master", false); Rec.Validate("Migrate Hist. AP Trx.", false); + Rec.Validate("Migrate Current 1099 Year", false); + Rec.Validate("Migrate Next 1099 Year", false); end; end; } @@ -408,6 +410,8 @@ table 47061 "SL Company Additional Settings" Rec.Validate("Migrate Item Classes", false); Rec.Validate("Migrate Vendor Classes", false); Rec.Validate("Migrate Only GL Master", false); + Rec.Validate("Migrate Current 1099 Year", false); + Rec.Validate("Migrate Next 1099 Year", false); if Rec."Migrate Inventory Module" then Rec.Validate("Migrate Only Inventory Master", true); @@ -477,6 +481,38 @@ table 47061 "SL Company Additional Settings" InitValue = false; ToolTip = 'Specify whether to include Resources with a Status of Hold.'; } + field(39; "Migrate Current 1099 Year"; Boolean) + { + Caption = 'Migrate Current 1099 Year'; + InitValue = false; + DataClassification = SystemMetadata; + + trigger OnValidate() + begin + if Rec."Migrate Current 1099 Year" then begin + Rec.Validate("Migrate Payables Module", true); + + if not Rec."Migrate GL Module" then + Rec.Validate("Migrate GL Module", true); + end; + end; + } + field(40; "Migrate Next 1099 Year"; Boolean) + { + Caption = 'Migrate Next 1099 Year'; + InitValue = false; + DataClassification = SystemMetadata; + + trigger OnValidate() + begin + if Rec."Migrate Next 1099 Year" then begin + Rec.Validate("Migrate Payables Module", true); + + if not Rec."Migrate GL Module" then + Rec.Validate("Migrate GL Module", true); + end; + end; + } } keys @@ -738,6 +774,18 @@ table 47061 "SL Company Additional Settings" exit(false); end; + internal procedure GetMigrateCurrent1099YearEnabled(): Boolean + begin + GetSingleInstance(); + exit(Rec."Migrate Current 1099 Year"); + end; + + internal procedure GetMigrateNext1099YearEnabled(): Boolean + begin + GetSingleInstance(); + exit(Rec."Migrate Next 1099 Year"); + end; + internal procedure AreAllModulesDisabled(): Boolean begin exit(not Rec."Migrate GL Module" diff --git a/Apps/W1/HybridSL/app/src/tables/SLSupportedTaxYear.Table.al b/Apps/W1/HybridSL/app/src/tables/SLSupportedTaxYear.Table.al new file mode 100644 index 0000000000..1e30b62bf4 --- /dev/null +++ b/Apps/W1/HybridSL/app/src/tables/SLSupportedTaxYear.Table.al @@ -0,0 +1,29 @@ +// ------------------------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ------------------------------------------------------------------------------------------------ + +namespace Microsoft.DataMigration.SL; + +table 47090 "SL Supported Tax Year" +{ + DataPerCompany = false; + Caption = 'Supported Tax Year'; + DataClassification = SystemMetadata; + + fields + { + field(1; "Tax Year"; Integer) + { + Caption = 'Tax Year'; + NotBlank = true; + } + } + keys + { + key(PK; "Tax Year") + { + Clustered = true; + } + } +} \ No newline at end of file From 8c8a4224729050fc32a3e476063251612668211f Mon Sep 17 00:00:00 2001 From: Brian Lauer Date: Thu, 6 Nov 2025 17:29:55 -0500 Subject: [PATCH 2/2] Add label for Gen Journal Batch description in Vendor 1099 data population --- .../app/src/codeunits/SLPopulateVendor1099Data.Codeunit.al | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Apps/W1/HybridSL/app/src/codeunits/SLPopulateVendor1099Data.Codeunit.al b/Apps/W1/HybridSL/app/src/codeunits/SLPopulateVendor1099Data.Codeunit.al index d671e99208..1c0ab3d21c 100644 --- a/Apps/W1/HybridSL/app/src/codeunits/SLPopulateVendor1099Data.Codeunit.al +++ b/Apps/W1/HybridSL/app/src/codeunits/SLPopulateVendor1099Data.Codeunit.al @@ -29,6 +29,7 @@ codeunit 47007 "SL Populate Vendor 1099 Data" SLNext1099Yr: Integer; Current1099YearOpen: Boolean; Next1099YearOpen: Boolean; + GenJournalBatchDescriptionTxt: Label 'SL Vendor 1099 Tax Journal for ', Locked = true; MessageCodeAbortedTxt: Label 'ABORTED', Locked = true; MessageCodeCompletedTxt: Label 'COMPLETED', Locked = true; MessageCodeSkippedTxt: Label 'SKIPPED', Locked = true; @@ -582,7 +583,7 @@ codeunit 47007 "SL Populate Vendor 1099 Data" GenJournalTemplate.Validate(Name, GenJournalBatchCode); GenJournalTemplate.Validate(Type, GenJournalTemplate.Type::General); GenJournalTemplate.Validate(Recurring, false); - GenJournalTemplate.Validate(Description, GenJournalBatchCode + '-' + 'SL Vendor 1099 Tax Journal for ' + Format(TaxYear)); + GenJournalTemplate.Validate(Description, GenJournalBatchDescriptionTxt + Format(TaxYear)); GenJournalTemplate.Insert(true); end; exit(GenJournalTemplate.Name); @@ -633,7 +634,7 @@ codeunit 47007 "SL Populate Vendor 1099 Data" var GenJournalBatch: Record "Gen. Journal Batch"; TemplateName: Code[10]; - GenJournalBatchDescriptionTxt: Label 'SL Vendor 1099 Tax Journal for ', Locked = true; + begin TemplateName := CreateGenJournalTemplateIfNeeded(GeneralJournalBatchCode, TaxYear); GenJournalBatch.SetRange("Journal Template Name", TemplateName);