Featured Post

Dynamics GP 2019 is now Released

It is now released, Microsoft Dynamics GP 2019 which moves the system to the modern product life cycle   " Beginning with the October...

Sunday, December 8, 2019

Automation Tool for Dynamics GP Management Reporter - Row Definition ( Auto Update MR Row Definition )

One of our customers requested to have their report definition of "Management Reporter" gets updated automatically whenever a change on the chart of accounts occurs. This can be simply automated by considering the "All Accounts" function of MR which by default retrieves all accounts to the row definition regardless of any criteria. A link will show up on the home page, on which you click and drill down for details of "account numbers".



Although, the case is different as we have to consider a specific representation of he GL account, each account should have the "Account number" first along with the "Account Description".

The structure includes Dynamics GP and Management Reporter data mart, which runs an integration between Dynamics GP database and MR DataMart, so that data can be structure in an easy and efficient way for reporting purposes. 


Technical Case Description:

Management reporter has two main components for every report, row definition and column definition. In our case the row definition includes the list of accounts in a "Static" manner, along with a "Total" row at the bottom. Here is a screen shot of how the "Row Definition" should look like as derived from Fabrikam GP Test Company 


MR Row Definition

Database Structure
Any row definition in MR is primarily defined in both [ControlRowCriteria] and [ControlRowDetail]. These two tables defines the criteria in which the row definition records are defined ( in addition to many other tables as well ) 


ControlRowCriteria Table:
  • RowLinkID
  • RowDetailID


ControlRowDetail Table:
  • ID ( Secondary Key for ControlRowCriteria - RowDetailID)
  • RowFormatID
  • RowNumber
  • RowCode


Automation Tool - MR Report Definition
The purpose of the tool is to retrieve any updates from Dynamics GP COA, and reflect the changes on the MR report "row" definition  automatically without. 
Important Note
  • The code above has been tested on a limited data sample, with a specific chart of account segmentation design. Therefore, do not run this on production environment.
  • The code below is built for a chart of account with three segment, different COA requires different segmentation

Part 1 - Master Details 



---- Log Table, for any new rows added to MR
CREATE TABLE [ManagementReporter].[Reporting].[GPEssentials_RowCode]
(
    [RowCode] [BIGINT] NULL,
    [RowFormatID] [UNIQUEIDENTIFIER] NULL,
    [MR_TimeStamp] [DATETIME] NULL
) ON [PRIMARY];

GO

--- Staging Master Table, to keep track of changes on COA
CREATE TABLE TWO.DBO.GPEssentials_NewAccounts
(
    AccountIndex [INT] NOT NULL,
    AccountNumber VARCHAR(MAX),
    SegmentOne VARCHAR(MAX),
    SegmentTwo VARCHAR(MAX),
    SegmentThree VARCHAR(MAX),
    AccountDescription VARCHAR(MAX),
    MR_AccountDescription VARCHAR(MAX),
    AddedToMR_Index INT,
    MR_TimeStamp DATETIME
);


--- Migrate all existing GL Accounts into the Master Table - Staging
INSERT INTO TWO.DBO.GPEssentials_NewAccounts
SELECT [Account Index],
       [Account Number],
       Segment1,
       Segment2,
       Segment3,
       [Account Description],
       CONCAT(RTRIM(LTRIM([Account Number])), ' ', RTRIM(LTRIM([Account Description]))),
       1,
       GETDATE()
FROM dbo.Accounts;

Part 2 - Procedures
Every table from the above two tables has a specific stored procedure to insert a record, these procedures are:



CREATE PROCEDURE [Reporting].[GPEssentials_InsertNew_ControlRowCriteria]
    @sp_RowLinkID AS [UNIQUEIDENTIFIER],
    @sp_RowDetailID AS [UNIQUEIDENTIFIER],
    @sp_Low AS VARCHAR(MAX),
    @sp_High AS VARCHAR(MAX),
    @sp_DimensionCode AS VARCHAR(MAX)
AS
INSERT INTO [Reporting].[ControlRowCriteria]
(
    [RowLinkID],
    [RowDetailID],
    [DisplayOrder],
    [IsSubtracted],
    [CriteriaType],
    [AccountSetID],
    [SegmentNumber],
    [DimensionCode],
    [SubCriteriaType],
    [Low],
    [High],
    [NamespaceID],
    [XbrlUnitID],
    [XbrlLinkRoleID],
    [XbrlIsNil]
)
VALUES
(@sp_RowLinkID, @sp_RowDetailID, 0, 0, 13, NULL, NULL, @sp_DimensionCode, '', @sp_Low, @sp_High, NULL, NULL, NULL, 0);

GO


CREATE PROCEDURE [Reporting].[GPEssentials_InsertNew_ControlRowDetail] @sp_RowFormatID AS [UNIQUEIDENTIFIER]
AS
DECLARE @sp_RowLinkID AS [UNIQUEIDENTIFIER];
DECLARE @sp_RowNumber AS INT;
DECLARE @sp_RowCode AS INT;
DECLARE @sp_Description AS VARCHAR(MAX);

SET @sp_RowLinkID = NEWID();
SET @sp_RowNumber =
(
    SELECT MAX(RowNumber) + 1
    FROM [Reporting].[ControlRowDetail]
    WHERE RowFormatID = @sp_RowFormatID
);
SET @sp_RowCode =
(
    SELECT MAX(RowCode) + 10
    FROM [Reporting].GPEssentials_RowCode
    WHERE RowFormatID = @sp_RowFormatID
);
SET @sp_Description = '';

INSERT INTO [Reporting].GPEssentials_RowCode
VALUES
(@sp_RowCode, @sp_RowFormatID, GETDATE());
INSERT INTO [Reporting].[ControlRowDetail]
(
    [ID],
    [RowFormatID],
    [RowNumber],
    [RowCode],
    [Description],
    [FormatCode],
    [RelatedRows],
    [FormatOverride],
    [NormallyCreditBalance],
    [ColumnRange],
    [FontStyleID],
    [IsNonPrinting],
    [UseCurrencySymbol],
    [SuppressInAccountDetail],
    [SuppressInTransactionDetail],
    [SuppressIfZero],
    [UseBlanksForZero],
    [SuppressRollup],
    [SuppressRounding],
    [HasFontStyleOverride],
    [OverrideFontName],
    [OverrideFontSize],
    [OverrideIsBold],
    [OverrideIsItalic],
    [OverrideIsUnderline],
    [OverrideIsStrikeThrough],
    [OverrideForegroundColor],
    [OverrideBackgroundColor],
    [OverrideIndent],
    [NoteID]
)
VALUES
(@sp_RowLinkID, @sp_RowFormatID, @sp_RowNumber, @sp_RowCode, @sp_Description, 0, '', '', 0, '',
 '00000000-0000-0000-0000-000000000000', 0, 0, 0, 0, 0, 0, 0, 0, 0, '', 0, 0, 0, 0, 0, 0, 0, 0, NULL);

GO



Part  3 - Add new row to MR report row definition


After creating the master tables from Part 1, and the procedures from Part 2, you are ready to run the code below which will update the row definition with any new accounts ( as derived from the new acconts table, which has AddedtoMRIndex as 0 ) only.
Make sure to change the parameters in the code below to reflect the correct RowLinkID and RowFormatID as derived from the specifci row definition you are updating.



--- Make sure to change the @RowFormatID and the @RowLinkID
--- as derived from the row definition you are updating

DECLARE @RowLinkID AS [UNIQUEIDENTIFIER]; --- Row Link ID and ID
DECLARE @RowFormatID AS [UNIQUEIDENTIFIER];
DECLARE @RowNumber AS INT;
DECLARE @RowCode AS INT;
DECLARE @Description AS VARCHAR(MAX);

DECLARE @RowDetailID AS [UNIQUEIDENTIFIER];
DECLARE @SegmentOne AS VARCHAR(MAX);
DECLARE @SegmentTwo VARCHAR(MAX);
DECLARE @SegmentThree AS VARCHAR(MAX);
DECLARE @Low AS VARCHAR(MAX);
DECLARE @High AS VARCHAR(MAX);
DECLARE @DimensionCode VARCHAR(MAX);


DECLARE @AccountIndex VARCHAR(MAX);
DECLARE @AccountNumber VARCHAR(MAX);
DECLARE @AccountDescription VARCHAR(MAX);
DECLARE @MR_AccountDescription VARCHAR(MAX);



--- Get "Total" record RowCode, RowNumber
SET @RowFormatID = 'BF060737-4BC0-4073-A183-841F388F4009';
SET @RowLinkID = '6E1CC8A4-D86F-449B-A719-AA2E9D54084E';
SET @RowNumber =
(
    SELECT RowNumber
    FROM [Reporting].[ControlRowDetail]
    WHERE [Description] = 'Total'
          AND RowFormatID = @RowFormatID
);
SET @RowCode =
(
    SELECT RowCode
    FROM [Reporting].[ControlRowDetail]
    WHERE [Description] = 'Total'
          AND RowFormatID = @RowFormatID
);

--- Remove "Total" record

UPDATE [Reporting].[ControlRowDetail]
SET [Description] = '',
    [FormatCode] = 0,
    [RelatedRows] = ''
WHERE [RowFormatID] = @RowFormatID
      AND RowNumber = @RowNumber
      AND RowCode = @RowCode;



DECLARE MR_CurSOR CURSOR LOCAL STATIC FORWARD_ONLY READ_ONLY FOR
SELECT TOP 1
    AccountIndex,
    RTRIM(LTRIM(AccountNumber)),
    RTRIM(LTRIM(SegmentOne)),
    RTRIM(LTRIM(SegmentTwo)),
    RTRIM(LTRIM(SegmentThree)),
    RTRIM(LTRIM(AccountDescription)),
    RTRIM(LTRIM(MR_AccountDescription))
FROM TWO.DBO.GPEssentials_NewAccounts
WHERE AddedToMR_Index = 0;

OPEN MR_CurSOR;

FETCH NEXT FROM MR_CurSOR
INTO @AccountIndex,
     @AccountNumber,
     @SegmentOne,
     @SegmentTwo,
     @SegmentThree,
     @AccountDescription,
     @MR_AccountDescription;

WHILE @@FETCH_STATUS = 0
BEGIN

    IF EXISTS
    (
        SELECT RowNumber
        FROM [Reporting].[ControlRowDetail]
        WHERE [Description] = ''
              AND RowFormatID = @RowFormatID
    )
    BEGIN
        SET @RowCode =
        (
            SELECT MIN(RowCode)
            FROM [Reporting].[ControlRowDetail]
            WHERE [Description] = ''
                  AND RowFormatID = @RowFormatID
        );
        SET @RowNumber =
        (
            SELECT MIN(RowNumber)
            FROM [Reporting].[ControlRowDetail]
            WHERE [Description] = ''
                  AND RowFormatID = @RowFormatID
        );

        SET @RowDetailID =
        (
            SELECT ID
            FROM [Reporting].[ControlRowDetail]
            WHERE RowFormatID = @RowFormatID
                  AND RowCode = @RowCode
                  AND RowNumber = @RowNumber
        );


        UPDATE [Reporting].[ControlRowDetail]
        SET [Description] = @MR_AccountDescription
        WHERE [RowFormatID] = @RowFormatID
              AND RowNumber = @RowNumber
              AND RowCode = @RowCode
              AND ID = @RowDetailID;

        -- Segment One
        SET @DimensionCode = 'Entity';
        EXECUTE [Reporting].GPEssentials_InsertNew_ControlRowCriteria @sp_RowLinkID = @RowLinkID,         -- uniqueidentifier
                                                                      @sp_RowDetailID = @RowDetailID,     -- uniqueidentifier
                                                                      @sp_Low = @SegmentOne,              -- varchar(max)
                                                                      @sp_High = @SegmentOne,             -- varchar(max)
                                                                      @sp_DimensionCode = @DimensionCode; -- varchar(max)

        -- Segment Two
        SET @DimensionCode = 'Division';
        EXECUTE [Reporting].GPEssentials_InsertNew_ControlRowCriteria @sp_RowLinkID = @RowLinkID,         -- uniqueidentifier
                                                                      @sp_RowDetailID = @RowDetailID,     -- uniqueidentifier
                                                                      @sp_Low = @SegmentTwo,              -- varchar(max)
                                                                      @sp_High = @SegmentTwo,             -- varchar(max)
                                                                      @sp_DimensionCode = @DimensionCode; -- varchar(max)

        -- Segment Three
        SET @DimensionCode = 'Natural Account';
        EXECUTE [Reporting].GPEssentials_InsertNew_ControlRowCriteria @sp_RowLinkID = @RowLinkID,         -- uniqueidentifier
                                                                      @sp_RowDetailID = @RowDetailID,     -- uniqueidentifier
                                                                      @sp_Low = @SegmentThree,            -- varchar(max)
                                                                      @sp_High = @SegmentThree,           -- varchar(max)
                                                                      @sp_DimensionCode = @DimensionCode; -- varchar(max)
        UPDATE TWO.DBO.GPEssentials_NewAccounts
        SET AddedToMR_Index = 1
        WHERE AccountIndex = @AccountIndex;

    END;
    ELSE
    BEGIN


        EXECUTE [Reporting].[GPEssentials_InsertNew_ControlRowDetail] @sp_RowFormatID = @RowFormatID;

        SET @RowCode =
        (
            SELECT MIN(RowCode)
            FROM [Reporting].[ControlRowDetail]
            WHERE [Description] = ''
                  AND RowFormatID = @RowFormatID
        );
        SET @RowNumber =
        (
            SELECT MIN(RowNumber)
            FROM [Reporting].[ControlRowDetail]
            WHERE [Description] = ''
                  AND RowFormatID = @RowFormatID
        );

        SET @RowDetailID =
        (
            SELECT ID
            FROM [Reporting].[ControlRowDetail]
            WHERE RowFormatID = @RowFormatID
                  AND RowCode = @RowCode
                  AND RowNumber = @RowNumber
        );


        UPDATE [Reporting].[ControlRowDetail]
        SET [Description] = @MR_AccountDescription
        WHERE [RowFormatID] = @RowFormatID
              AND RowNumber = @RowNumber
              AND RowCode = @RowCode
              AND ID = @RowDetailID;

        -- Segment One
        SET @DimensionCode = 'Entity';
        EXECUTE [Reporting].GPEssentials_InsertNew_ControlRowCriteria @sp_RowLinkID = @RowLinkID,         -- uniqueidentifier
                                                                      @sp_RowDetailID = @RowDetailID,     -- uniqueidentifier
                                                                      @sp_Low = @SegmentOne,              -- varchar(max)
                                                                      @sp_High = @SegmentOne,             -- varchar(max)
                                                                      @sp_DimensionCode = @DimensionCode; -- varchar(max)

        -- Segment Two
        SET @DimensionCode = 'Division';
        EXECUTE [Reporting].GPEssentials_InsertNew_ControlRowCriteria @sp_RowLinkID = @RowLinkID,         -- uniqueidentifier
                                                                      @sp_RowDetailID = @RowDetailID,     -- uniqueidentifier
                                                                      @sp_Low = @SegmentTwo,              -- varchar(max)
                                                                      @sp_High = @SegmentTwo,             -- varchar(max)
                                                                      @sp_DimensionCode = @DimensionCode; -- varchar(max)

        -- Segment Three
        SET @DimensionCode = 'Natural Account';
        EXECUTE [Reporting].GPEssentials_InsertNew_ControlRowCriteria @sp_RowLinkID = @RowLinkID,         -- uniqueidentifier
                                                                      @sp_RowDetailID = @RowDetailID,     -- uniqueidentifier
                                                                      @sp_Low = @SegmentThree,            -- varchar(max)
                                                                      @sp_High = @SegmentThree,           -- varchar(max)
                                                                      @sp_DimensionCode = @DimensionCode; -- varchar(max)
        UPDATE TWO.DBO.GPEssentials_NewAccounts
        SET AddedToMR_Index = 1
        WHERE AccountIndex = @AccountIndex;

    END;

    FETCH NEXT FROM MR_CurSOR
    INTO @AccountIndex,
         @AccountNumber,
         @SegmentOne,
         @SegmentTwo,
         @SegmentThree,
         @AccountDescription,
         @MR_AccountDescription;
END;

CLOSE MR_CurSOR;
DEALLOCATE MR_CurSOR;



---- Add "Total" record to MR

IF EXISTS
(
    SELECT RowNumber
    FROM [Reporting].[ControlRowDetail]
    WHERE [Description] = ''
          AND RowFormatID = @RowFormatID
)
BEGIN
    SET @RowCode =
    (
        SELECT MIN(RowCode)
        FROM [Reporting].[ControlRowDetail]
        WHERE [Description] = ''
              AND RowFormatID = @RowFormatID
    );
    SET @RowNumber =
    (
        SELECT MIN(RowNumber)
        FROM [Reporting].[ControlRowDetail]
        WHERE [Description] = ''
              AND RowFormatID = @RowFormatID
    );


    UPDATE [Reporting].[ControlRowDetail]
    SET [Description] = 'Total',
        [FormatCode] = 2,
        [RelatedRows] = CONCAT('10:', @RowCode - 10)
    WHERE [RowFormatID] = @RowFormatID
          AND RowNumber = @RowNumber
          AND RowCode = @RowCode;

END;
ELSE
BEGIN


    EXECUTE [Reporting].[GPEssentials_InsertNew_ControlRowDetail] @sp_RowFormatID = @RowFormatID;

    SET @RowCode =
    (
        SELECT MIN(RowCode)
        FROM [Reporting].[ControlRowDetail]
        WHERE [Description] = ''
              AND RowFormatID = @RowFormatID
    );
    SET @RowNumber =
    (
        SELECT MIN(RowNumber)
        FROM [Reporting].[ControlRowDetail]
        WHERE [Description] = ''
              AND RowFormatID = @RowFormatID
    );

    SET @RowDetailID =
    (
        SELECT ID
        FROM [Reporting].[ControlRowDetail]
        WHERE RowFormatID = @RowFormatID
              AND RowCode = @RowCode
              AND RowNumber = @RowNumber
    );

    UPDATE [Reporting].[ControlRowDetail]
    SET [Description] = 'Total',
        [FormatCode] = 2,
        [RelatedRows] = CONCAT('10:', @RowCode - 10)
    WHERE [RowFormatID] = @RowFormatID
          AND RowNumber = @RowNumber
          AND RowCode = @RowCode
          AND ID = @RowDetailID;

END;





Download Links



Best Regards, 
Mahmoud M. AlSaadi

29 comments:

  1. we will give it a try and see how it works :)

    ReplyDelete
  2. Its working Perfectly ..... Thank You Mahmoud.

    ReplyDelete
  3. The purpose of the tool is to retrieve any updates from Dynamics GP COA, and reflect the changes on the MR report "row" definition automatically without.

    This doesn't make sense.

    Are you suggesting this as an alternative to rebuilding the datamart?

    ReplyDelete
    Replies
    1. Not at all, this is specifically built for automating certain manual tasks which are applied on the report definition, not the data.

      Best Regards,
      Mahmoud M. AlSaadi

      Delete
    2. The grammar in the "Automation Tool - MR Report Definition" section doesn't make sense. How about a clear definition of what the tool does.

      Delete
  4. Hey I’m Martin Reed,if you are ready to get a loan contact.Mr Benjamin via email: lfdsloans@lemeridianfds.com,WhatsApp:+1 989-394-3740 I’m giving credit to Le_Meridian Funding Service .They grant me the sum 2,000,000.00 Euro. within 5 working days. Le_Meridian Funding Service  is a  group investors into pure loan and debt financing at the  returns of 1.9% to pay off your bills or buy a home Or Increase your Business. please I advise everyone out there who are in need of loan and can be reliable, trusted and capable of repaying back at the due time of funds.

    ReplyDelete
  5. Financial Restoration Through the help of benjamin lee. Email:  247officedept@gmail.com that's his Email and this is his whatsapp number  +1-989-394-3740 . I'mLeonardo Hugo a agronist who was able to revive his dying Livestock Feed Manufacturing through the help of a GodSent lender known as Benjamin Briel Lee the Loan Officer. I want you to know that his Service is the right place for you to resolve all your financial problem because am a living testimony and I can't just keep this to myself when others are looking for a way to be financially lifted. I want you all to contact this God sent lender using the details as stated in other to be a partaker of this great opportunity and also they work with good/reputable bank that wire money transfer without delay into my account.

    ReplyDelete


  6. That is nice article from you, this is informative stuff. Hope more articles from you . I also want to share some information about Salesforce CPQ Training

    ReplyDelete
  7. BE SMART AND BECOME RICH IN LESS THAN 3DAYS....It all depends on how fast 
    you can be to get the new PROGRAMMED blank ATM card that is capable of
    hacking into any ATM machine,anywhere in the world. I got to know about 
    this BLANK ATM CARD when I was searching for job online about a month 
    ago..It has really changed my life for good and now I can say I'm rich and 
    I can never be poor again. The least money I get in a day with it is about 
    $50,000.(fifty thousand USD) Every now and then I keeping pumping money 
    into my account. Though is illegal,there is no risk of being caught 
    ,because it has been programmed in such a way that it is not traceable,it 
    also has a technique that makes it impossible for the CCTVs to detect 
    you..For details on how to get yours today, email the hackers on : (
    atmmachinehackers1@gmail.com ). Tell your 
    loved once too, and start to live large. That's the simple testimony of how 
    my life changed for good...Love you all ...the email address again is ;
    atmmachinehackers1@gmail.com

    ReplyDelete
  8. Very informative blog with a lot of information. Thank you. The Customize Boxes manufactures stylish packaging for Custom Candle Boxes at wholesale prices and discounts with free shipping services in all across USA.

    ReplyDelete
  9. The need for more legitimate hacking companies are rising each passing day because of the magnitude of scams being run on the Internet. I know what I went through before I was able to contact Osecybersailing to work on my case after getting scammed multiple times and trying to retrieve back my lost investment funds. After I have invested $670k usd with a fraudulent crypto investment company and i lost everything in the space of one month and got scammed again and again trying to find a solution which wasn't easy to come by given the circumstances that brought about the incident, getting scammed multiple times is never a good experience for anyone involved which was what exactly happened to me when I got scammed investing into crypto currency. Many posing on the Internet as hacking companies aren't really what they are because most of them are scammers waiting for a prey to devour, I got referred to reach out to Osecybersailing through a friend and after I investigated about their legitimacy I came across many of their testimony from their satisfied clients that got their lost investment funds retrieved working with this legitimate and evergreen hacking institute which also changed my story helping me retrieve back my lost investment funds. Many of the testimony really were saying great and worthy things about how the company saved them from financial loss, I decided to hire them after many consideration and they didn't disappoint me by making the choice of choosing them after what I have experienced lately trying to get my funds back from the scammers. They proved they can be trusted with the recovery of lost investment funds which is why I wrote this down for other victims of scam who wishes to ever retrieve back their lost investment funds to let Osecybersailing solve their case, they're worthy and has what it takes to restore back your funds and peace of mind. If you also wish to check your partner Whatsapp conversation to know if he/she is cheating then Osecybersailing is the best team for you.
    If you find yourself lost in the depths of lost Bitcoin, facebook and Whatsapp hacking to catch your cheating partner, let Osecybersailing's team guide you towards the light of redemption.
    Facebook page: Osecybersailing
    Email: osecybersailing@cyberservices.com

    ReplyDelete
  10. This comment has been removed by the author.

    ReplyDelete
  11. This article sheds light on the significance of ibm 59h4364, and I must say, it's quite intriguing. It's fascinating to learn about these lesser-known components that play a crucial role in the bigger picture. Thanks for sharing this eye-opening piece!

    ReplyDelete
  12. Hey there! Just stumbled upon this article and couldn't help but drop a comment. I've been eyeing845628-B21's technology products for a while now, and they never disappoint. From performance to reliability, they've got it all covered. Can't wait to see what they come up with next!

    ReplyDelete
  13. This comment has been removed by the author.

    ReplyDelete
  14. This article about IBM 59H4364 is so informative! I've been curious about its features and specifications, and your explanation has really cleared things up for me. It's fascinating to see how IBM continues to innovate and push the boundaries of technology. Thanks for sharing this valuable insight!

    ReplyDelete
  15. Great read! I had no idea how much of an impact the tablet cialis 20 mg could have. It's good to know the potential benefits for those dealing with certain conditions. Thanks for breaking it down in such an easy-to-understand way!






    ReplyDelete
  16. Thanks for the info! I've been looking for the price of everlong 60 mg tablet price in pakistanand it's good to finally get some details. Do you happen to know if there are any cheaper alternatives or is this pretty much the standard?






    ReplyDelete
  17. Great read! I've heard a lot about tablet cialis 5mg and how it helps with more than just physical performance. It's interesting to see how it's being used as a daily option with fewer side effects. Definitely something worth considering for anyone looking to maintain a balanced approach to their health. Thanks for the insight!






    ReplyDelete
  18. Great article! I completely agree—mangagago is such a convenient platform for manga lovers, especially when it comes to keeping up with the latest releases. It’s awesome how accessible it is for exploring different genres. Do you have any favorite series you’d recommend reading there?

    ReplyDelete
  19. Great read! I’ve often wondered about the safety of sites like is manganato safe, especially since there are so many mixed reviews out there. It’s super helpful to have a breakdown of the risks versus benefits. Thanks for highlighting the importance of using ad blockers and staying cautious about suspicious links—definitely something to keep in mind.

    ReplyDelete
  20. Great article! It's awesome to see services like free scrap metal pick up central coast available on the Central Coast. Not only is it convenient, but it also helps keep the environment cleaner by recycling metals that would otherwise just go to waste. Thanks for spreading the word!

    ReplyDelete
  21. Great article! price of scrap copper is such an exciting but complex world. It’s amazing how much goes into every book that ends up on our shelves—whether it's editing, cover design, or the actual marketing strategy to get it in front of readers. As a reader, I never realized the effort behind each page until I started learning more about the industry. Thanks for breaking it down so well!

    ReplyDelete
  22. This article brings such an important perspective to a very complex issue. It's human rights in Palestine and Israel to see the ongoing struggles on both sides, especially when it comes to fundamental human rights. I really appreciate how the article highlights the need for empathy and understanding, which can often get lost in the broader political debates. It’s clear that every individual, regardless of where they are, deserves safety, dignity, and respect.

    ReplyDelete
  23. I totally agree with you! mangagago has been such a lifesaver for so many manga fans who want quick access to their favorite series. The layout and ease of use make it a go-to spot for readers. Thanks for highlighting it in your article!






    ReplyDelete
  24. Hey there! Loved your article on manganata. It was such a refreshing dive into the topic, and I really appreciated how you broke down its origins and the cultural significance. I didn’t know how unique the blending process is to achieve that gradient effect! Now I can’t wait to try making one myself. Thanks for sharing such an interesting read!

    ReplyDelete
  25. Great read! instagram marketing appkodhas definitely become essential for any brand wanting to connect with its audience in a fun and visually engaging way. Appkod seems like a fantastic tool for those looking to streamline their strategy and keep up with all the trends. Thanks for sharing these insights—excited to try it out!

    ReplyDelete