After creating a custom projinvoice can't find created design - axapta

I need some help here. I created a custom report invoice design for PSAProjInvoice.
I did duplicate PSAProjInvoice and worked on a already made design.
Created a Controller and PrintMgmtDocTypeHandler class.
Created outputitem extension and redirected it to my ProjInvoiceController
In axapta in ProjFormletterParameters form parameters it shows me name of my custom report but when I go to project invoices and try to make a look at the invoice I just get a error: Unable to find the report design PSAProjInvoiceSZM.ReportPL.
class PSAProjInvoiceSZM
{
[PostHandlerFor(classStr(PSAProjAndContractInvoiceController),
staticMethodStr(PSAProjAndContractInvoiceController, construct))]
public static void ReportNamePostHandler(XppPrePostArgs arguments)
{
PSAProjAndContractInvoiceController controller = arguments.getReturnValue();
controller.parmReportName(ssrsreportstr(PSAprojinvoiceSZM, Report));
}
}
I think that it's a problem with my controller class because I actually have no idea how it should look like. Tried to make one based on salesinvoice tutorial found on microsoft docs but it didn't help me at all.
Tried to make it based on this article:
https://blogs.msdn.microsoft.com/dynamicsaxbi/2017/01/01/how-to-custom-designs-for-business-docs/
My Controller:
class ProjInvoiceControllerSZM extends PSAProjAndContractInvoiceController
{
public static ProjInvoiceControllerSZM construct()
{
return new ProjInvoiceControllerSZM();
}
public static void main(Args _args)
{
SrsReportRunController formLetterController =
ProjInvoiceControllerSZM::construct();
ProjInvoiceControllerSZM controller = formLetterController;
controller.initArgs(_args);
Controller.parmReportName(ssrsReportStr(PSAProjInvoiceSZM, Report));
/* if (classIdGet(_args.caller()) ==
classNum(PurchPurchOrderJournalPrint))
{
formLetterController.renderingCompleted +=
eventhandler(PurchPurchOrderJournalPrint::renderingCompleted);
}*/
formLetterController.startOperation();
}
protected void outputReport()
{
SRSCatalogItemName reportDesign;
reportDesign = ssrsReportStr(PSAProjInvoiceSZM,Report);
this.parmReportName(reportDesign);
this.parmReportContract().parmReportName(reportDesign);
formletterReport.parmReportRun().settingDetail().parmReportFormatName(reportDesign);
super();
}
}

Related

Resolve named registration dependency in Unity with runtime parameter

I have a following problem. I register my components and initialize them in Unity like this (example is for a Console application):
public class SharePointBootstrapper : UnityBootstrapper
{
...
public object Initialize(Type type, object parameter) =>
Container.Resolve(type,
new DependencyOverride<IClientContext>(Container.Resolve<IClientContext>(parameter.ToString())),
new DependencyOverride<ITenantRepository>(Container.Resolve<ITenantRepository>(parameter.ToString())));
public void RegisterComponents()
{
Container
.RegisterType<IClientContext, SharePointOnlineClientContext>(SharePointClientContext.Online.ToString())
.RegisterType<IClientContext, SharePointOnPremiseClientContext>(SharePointClientContext.OnPremise.ToString())
.RegisterType<ITenantRepository, DocumentDbTenantRepository>(SharePointClientContext.Online.ToString())
.RegisterType<ITenantRepository, JsonTenantRepository>(SharePointClientContext.OnPremise.ToString());
}
}
public enum SharePointClientContext
{
Online,
OnPremise
}
class Program
{
static void Main(string[] args)
{
...
bootstrap.RegisterComponents();
var bla = bootstrap.Initialize(typeof(ISharePointManager), SharePointClientContext.Online);
}
}
So, I register my components in MVC, WCF, Console etc. once with RegisterComponents() and initialize them with Initialize().
My question is, if I want to initialize specific named registration at runtime, from e.g. user input, can it be done otherwise as the code presented (with InjectionFactory or similar)?
This code works fine, but I'm not happy with its implementation. I have a feeling that it could be written in RegisterComponents() instead of Initialize() so that it accepts a parameter of some type, but I don't know how to do it.
Or, is maybe my whole concept wrong? If so, what would you suggest? I need to resolve named registration from a parameter that is only known at runtime, regardless of the technology (MVC, WCF, Console, ...).
Thanks!
Instead of doing different registrations, I would do different resolves.
Let's say that you need to inject IClientContext, but you want different implementations depending on a runtime parameter.
I wrote a similiar answer here. Instead of injecting IClientContext, you could inject IClientContextFactory, which would be responsible for returning the correct IClientContext. It's called Strategy Pattern.
public interface IClientContextFactory
{
string Context { get; } // Add context to the interface.
}
public class SharePointOnlineClientContext : IClientContextFactory
{
public string Context
{
get
{
return SharePointClientContext.Online.ToString();
}
}
}
// Factory for resolving IClientContext.
public class ClientContextFactory : IClientContextFactory
{
public IEnumerable<IClientContext> _clientContexts;
public Factory(IClientContext[] clientContexts)
{
_clientContexts = clientContexts;
}
public IClientContext GetClientContext(string parameter)
{
IClientContext clientContext = _clientContexts.FirstOrDefault(x => x.Context == parameter);
return clientContext;
}
}
Register them all, just as you did. But instead of injecting IClientContext you inject IClientContextFactor.
There also another solution where you use a Func-factory. Look at option 3, in this answer. One may argue that this is a wrapper for the service locator-pattern, but I'll leave that discussion for another time.
public class ClientContextFactory : IClientContextFactory
{
private readonly Func<string, IClientContext> _createFunc;
public Factory(Func<string, IClientContext> createFunc)
{
_createFunc = createFunc;
}
public IClientContext CreateClientContext(string writesTo)
{
return _createFunc(writesTo);
}
}
And use named registrations:
container.RegisterType<IClientContext, SharePointOnlineClientContext>(SharePointClientContext.Online.ToString());
container.RegisterType<IClientContext, SharePointOnPremiseClientContext>(SharePointClientContext.OnPremise.ToString());
container.RegisterType<IFactory, Factory>(
new ContainerControlledLifetimeManager(), // Or any other lifetimemanager.
new InjectionConstructor(
new Func<string, IClientContext>(
context => container.Resolve<IClientContext>(context));
Usage:
public class MyService
{
public MyService(IClientContextFactory clientContextFactory)
{
_clientContextFactory = clientContextFactory;
}
public void DoStuff();
{
var myContext = SharePointClientContext.Online.ToString();
IClientContextclientContext = _clientContextFactory.CreateClientContext(myContext);
}
}

Is there any way to pass Output of one workflow to another without using outarguments?

I have recently started working with Workflows.I am able to pass output of one activity as input to another through making use of OutArgument .Is it possible without using OutArgument.
If Possible please suggest me how?
Thanks all
You can use a workflow extension to act as a repository of variables in the scope of the whole workflow.
Create a workflow extension that contains properties.
Add the extension to the workflow application.
Set or Get the value of the properties from within Activities.
See https://msdn.microsoft.com/en-us/library/ff460215(v=vs.110).aspx
In response to your comment below.
You are wrong in your assumption. The extension "holds" the output from activity 1 which is then available to activity 2.
For example:
Create a class to hold properties:
public class PropertyStoreExtension
{
int _myProperty
public int MyProperty
{
get
{
return this._myProperty;
}
set
{
this._myProperty = value;
}
}
}
Add this as an extension to your workflow:
PropertyStoreExtension propertyStoreExtension = new PropertyStoreExtension
WorkflowInvoker myWorkflowInstence = new
WorkflowInvoker(myWorkflowDefinition());
myWorkflowInstence.Extensions.Add(propertyStoreExtension);
myWorkflowInstence.Invoke()
Your workflow contains 2 activities:
The first takes its "output" and stores it in the extension.
public class Activity1_SetProperty: CodeActivity
{
protected override void Execute(CodeActivityContext context)
{
PropertyStoreExtension pse =context.GetExtension<PropertyStoreExtension>();
if (pse != null)
{
pse.MyProperty=outputValue;
}
}
}
The second gets the value out of the extension.
public class Activity2_GetProperty: CodeActivity
{
protected override void Execute(CodeActivityContext context)
{
PropertyStoreExtension pse =context.GetExtension<PropertyStoreExtension>();
if (pse != null)
{
int intputValue; = pse.MyProperty
}
}
}

Nancyfx using Json.net, registering dependencies error

I'm trying to use nancy with JSON.net, follow the 2 ways that i found to register the dependencies but all way get me to an InvalidOperationException with a message "Something went wrong when trying to satisfy one of the dependencies during composition, make sure that you've registered all new dependencies in the container and inspect the innerexception for more details." with an inner exection of {"Unable to resolve type: Nancy.NancyEngine"}.
I'm using self hosting to run nancy and jeep everything really simple to been able just to test.
public static void Main(string[] args)
{
try
{
var host = new NancyHost(new Uri("http://localhost:8888/"));
host.Start(); // start hosting
Console.ReadKey();
host.Stop(); // stop hosting
}
catch
{
throw;
}
}
First I create a customSerializer
public class CustomJsonSerializer : JsonSerializer
{
public CustomJsonSerializer()
{
ContractResolver = new CamelCasePropertyNamesContractResolver();
Formatting = Formatting.Indented;
}
}
and then i tried 2 ways of registering
Using IRegistrations:
public class JsonRegistration : IRegistrations
{
public IEnumerable<TypeRegistration> TypeRegistrations
{
get
{
yield return new TypeRegistration(typeof(JsonSerializer), typeof(CustomJsonSerializer));
}
}
public IEnumerable<CollectionTypeRegistration> CollectionTypeRegistrations { get; protected set; }
public IEnumerable<InstanceRegistration> InstanceRegistrations { get; protected set; }
}
And also using Bootstrapper
public class NancyBootstrapper : DefaultNancyBootstrapper
{
protected override void ConfigureApplicationContainer(TinyIoCContainer container)
{
base.ConfigureApplicationContainer(container);
container.Register<JsonSerializer, CustomJsonSerializer>();
}
}
Which means that when self hosting I add the custom bootstrapper
var host = new NancyHost(new Uri("http://localhost:8888/"), new NancyBootstrapper());
Both way return the same error.
Problem is actually the versions, the nancy json.net package is using Newton.Json 6.0.0.0, BUT when installing the package it will install automatically newer version that will create this problem. Not sure what has change in the Newton.JSON that will actually create this.
https://github.com/NancyFx/Nancy.Serialization.JsonNet/issues/27
Just to add my hard won knowledge in this area, after a greatly frustrating few hours using Nancy 1.4.1.
If you use a custom bootstrapper, make sure you make the call to base.ConfigureApplicationContainer(container); before you start your custom registrations.
So, not:
public class MyCustomBootstrapper : DefaultNancyBootstrapper
{
protected override void ConfigureApplicationContainer(TinyIoCContainer container)
{
// MY BITS HERE...
base.ConfigureApplicationContainer(container);
}
}
but,
public class MyCustomBootstrapper : DefaultNancyBootstrapper
{
protected override void ConfigureApplicationContainer(TinyIoCContainer container)
{
base.ConfigureApplicationContainer(container); // Must go first!!
// MY BITS HERE...
}
}
If you don't do this you will get the following error:
Something went wrong when trying to satisfy one of the dependencies
during composition, make sure that you've registered all new
dependencies in the container and inspect the innerexception for more
details.
with a helpful inner exception of:
Unable to resolve type: Nancy.NancyEngine
The solution of changing the order of these C# statements was actually alluded to in #StevenRobbins' excellent answer here (which I could have saved myself several hours of pain if I'd only read properly the first time).
Says Steven:
By calling "base" after you've made a manual registration you are
effectively copying over your original registration by autoregister.
Either don't call base, or call it before you do your manual
registrations.

SysOperation Framework - CanGoBatchJournal

When canGoBatchJournal returns true, a RunBaseBatch can be created in Ax via the System administartion > Inquiries > Batch > New > Task > New >[ClassName:MyRunBaseBatch].
I have a couple of features which have been created using the SysOperation framework however. This method doesn't inherit the canGoBatchJournal method. Is there a way to make them visible in the above mentioned menu as well?
I took a dive into how to form control retrieves it's data. There is an SysOperationJournaledParametersAttribute attribute which you can use.
Below is an example of how the attribute would be applied to a controller. This example shows how the controller calls the custom service. The controller can then be used in as a batch task or you could call the controller from a menu to get the batch dialog to display.
[SysOperationJournaledParametersAttribute(true)]
class YourCustomController extends SysOperationServiceController
{
public void new()
{
super();
this.parmClassName(classStr(YourCustomService));
this.parmMethodName(methodStr(YourCustomService,processOperation));
this.parmDialogCaption("dialog caption");
}
public ClassDescription caption()
{
return "class description";
}
public static void main(Args args)
{
YourCustomController controller;
controller = new YourCustomController();
controller.startOperation();
}
}
Below would be the custom service the controller calls.
class YourCustomToolService extends SysOperationServiceBase
{
public void processOperation()
{
// Call your code to do run your custom logic
}
}
If you implement the SysOperation framework, it should already be good as SysOperationController implements the Batchable interface.
You can refer to this white paper: https://www.microsoft.com/en-us/download/details.aspx?id=29215

DropCreateDataBaseAlways is not working when working with multiple db schemas with Entity Framework 6 Code First

After watching the "Enhancements to Code First Migrations: Using HasDefaultSchema and ContextKey for Multiple Model Support" section of Julie Lerman's PluralSite video, "Entity Framework 6: Ninija Edition-What's New in EF 6" (https://app.pluralsight.com/library/courses/entity-framework-6-ninja-edition-whats-new/table-of-contents), it seems there is a way to run multiple schemas under a single database in Entity Framwork 6 using Code First Migrations...
However, based on the video you still need to these package manager commands for each project that houses a separate context:
1. enable-migrations
2. add-migration [MIGRATION NAME]
3. update-database
This is fine and good if you actually care about maintaining migrations going forward, which is not a concern of mine.
What I'd like to do is have each of my Context's initializers set to DropCreateDatabaseAlways, and when I start up my client app (in this case, an MVC site), code first will create the database for the first context used, create the tables in with the correct schema for that context, and then create the tables for the rest of the contexts with the correct schema.
I don't mind if the whole database is dropped and recreated every time I hit F5.
What is happening now is the last context that is accessed in the client app is the only context tables that are created in the database... any contexts being accessed before the last get their tables blown away.
I am currently using two contexts, a Billing context and a Shipping context.
Here is my code:
My client app is an MVC website, and its HomeController's Index method looks like this:
public ActionResult Index()
{
List<Shipping.Customer>
List<Billing.Customer> billingCustomers;
using (var shippingContext = new Shipping.ShippingContext())
{
shippingCustomers = shippingContext.Customers.ToList();
}
using (var billingContext = new Billing.BillingContext())
{
billingCustomers = billingContext.Customers.ToList();
}
}
Here is my DbMigrationsConfigurationClass and ShippingContext class for the Shipping Context:
internal sealed class Configuration : DbMigrationsConfiguration<ShippingContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
}
protected override void Seed(ShippingContext context)
{
}
}
public class ShippingContext : DbContext
{
public ShippingContext() : base("MultipleModelDb")
{
}
static ShippingContext()
{
Database.SetInitializer(new ShippingContextInitializer());
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.HasDefaultSchema("Shipping");
base.OnModelCreating(modelBuilder);
}
public DbSet<Customer> Customers { get; set; }
class ShippingContextInitializer : DropCreateDatabaseAlways<ShippingContext>
{
}
}
Likewise, here is the DbMigrationConfiguration class for the Billing Context and the BillingContext class:
internal sealed class Configuration : DbMigrationsConfiguration<BillingContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
}
protected override void Seed(BillingContext context)
{
}
}
public class BillingContext : DbContext
{
public BillingContext() : base("MultipleModelDb")
{
}
static BillingContext()
{
Database.SetInitializer(new BillingContextInitializer());
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.HasDefaultSchema("Billing");
base.OnModelCreating(modelBuilder);
}
public DbSet<Customer> Customers { get; set; }
class BillingContextInitializer : DropCreateDatabaseAlways<BillingContext>
{
}
}
based on the order that the contexts are being called in the controller's action method, whichever context is accessed last is the only context that is created... the other context is wiped out.
I feel like what I'm trying to do is very simple, yet code first migrations, as well as trying to "shoehorn" Entity Framework to represent multiple contexts as separate schemas in the same physical database seems a bit "hacky"...
I'm not that versed with migrations to begin with, so what I'm trying to do might not make any sense at all.
Any feedback would be helpful.
Thanks,
Mike

Resources