Acumatica - Approach to custom screen to link GL and Sub Accounts for custom restrictions - customization

The requirement is to build a custom form tab screen that starts of with an account selector. After choosing the account, the bottom grid is populated with a list of custom DAC's that basically consists of a key of CompanyID, AccountID, SubID that is stored in a custom table. The existing Account DAC does not have a selector component, which I can use for the top selection. The solution I tried to get past this is to create a new DAC but with only one unbound property, but this is giving me issues on the BQL side when setting up the Master/Detail data views for the screen. I will then attempt to setup a master detail relationship between my custom DAC and the Account DAC. Is this the best approach to build this screen, or is there another which I haven't thought of?
Screen Graph (ignore the where and filters, just doing a proof of concept first):
using System;
using PX.Data;
using PX.Data.BQL.Fluent;
using PX.Objects.GL;
namespace IP_GLRestrictions2
{
public class IPGLAcctToSubLinkMaint : PXGraph<IPGLAcctToSubLinkMaint>
{
public PXSave<IPAccount> Save;
public PXCancel<IPAccount> Cancel;
public PXFilter<IPAccount> MasterView;
public PXFilter<IPAcctSubLink> DetailsView;
public SelectFrom<IPAccount>.View AccountView;
public SelectFrom<IPAcctSubLink>
//.Where<IPAcctSubLink.accountID.IsEqual<IPAccount.accountID.FromCurrent>>
.View SubLinkView;
}
}
Custom DAC ( I want to show the Description and Active as unbound fields as soon as a subaccount ID is chosen in the details grid):
[Serializable]
[PXCacheName("Account To Subaccount Link")]
public class IPAcctSubLink : IBqlTable
{
#region AccountID
[PXDBInt(IsKey = true)]
[PXUIField(DisplayName = "Account ID")]
[PXDBDefault(typeof(IPAccount.accountID))]
[PXParent(typeof(SelectFrom<IPAccount>.Where<IPAccount.accountID.IsEqual<IPAcctSubLink.accountID.FromCurrent>>))]
////[PXSelector(typeof(Search<IPAccount.accountID>),
//// typeof(Account.accountCD),
//// typeof(Account.description),
//// SubstituteKey = typeof(Account.accountCD))]
public virtual int? AccountID { get; set; }
public abstract class accountID : PX.Data.BQL.BqlInt.Field<accountID> { }
#endregion
#region Subid
[PXDBInt(IsKey = true)]
[PXUIField(DisplayName = "Subaccount ID")]
[PXSelector(typeof(Search<Sub.subID>),
typeof(Sub.subCD),
typeof(Sub.description),
typeof(Sub.active),
SubstituteKey = typeof(Sub.subCD))]
public virtual int? Subid { get; set; }
public abstract class subid : PX.Data.BQL.BqlInt.Field<subid> { }
#endregion
#region Description
[PXString]
[PXUIField(DisplayName = "Description")]
//[PXDefault(typeof(Search<Sub.description, Where<Sub.subID, Equal<Current<IPAcctSubLink.subid>>>>))]
public virtual int? SubDescription { get; set; }
public abstract class subdescription : PX.Data.BQL.BqlInt.Field<subdescription> { }
#endregion
#region Active
[PXBool]
[PXUIField(DisplayName = "Active")]
//[PXDefault(typeof(Search<Sub.active, Where<Sub.subID, Equal<Current<IPAcctSubLink.subid>>>>))]
public virtual int? SubActive { get; set; }
public abstract class subActive : PX.Data.BQL.BqlInt.Field<subActive> { }
#endregion

If you just need a selector for a specific field in a DAC the easiest way to do that would be via a cache extension.
You can simply add the PXSelector attribute inside the cache extension.
public sealed class CacheExtension : PXCacheExtension<YourDAC>{
public static bool IsActive() => true;
[YourSelectorAttribute]
public int? AccountID { get; set; }
}

Related

Filter in Sitecore Children in Sitecore

I have this tree structure:
Page1
PageA
PageX
PageY
PageB
Page2
I want Page1 and Page2 as well as all child pages. I have created two classes. In one class using sitecore query
[SitecoreQuery("../*[##templateid={GUID}]", IsRelative = true)]
public virtual IEnumerable<ItemModel> Links { get; set; }
In other ItemModel class getting child pages
public class ItemModel
{
[SitecoreId]
public Guid Id { get; set; }
[SitecoreChildren]
public IEnumerable<SideMenuModel> Children { get; set; }
}
This is working fine but now I want to get only those child pages who have some specific template. Please provide me some solution.
It looks like you have most of the code correct but you need to also apply restrictions to the Children property on the ItemModel class.
You can use a SitecoreQuery on the like you have on the parent model:
public class ItemModel
{
[SitecoreId]
public Guid Id { get; set; }
[SitecoreQuery("./*[##templateid={SideMenuModel-GUID}]", IsRelative = true)]
public virtual IEnumerable<SideMenuModel> ChildItems { get; set; }
}
Or you can use the EnforceTemplate attribute on your SideMenuModel class:
[SitecoreType(TemplateId = "GUID", EnforceTemplate = SitecoreEnforceTemplate.Template)]
public class SideMenuModel
{
[SitecoreId]
public Guid Id { get; set; }
}
public class ItemModel
{
[SitecoreId]
public Guid Id { get; set; }
[SitecoreChildren]
public virtual IEnumerable<SideMenuModel> ChildItems { get; set; }
}
Since you have set EnforceTemplate then using the [SitecoreChildren] attribute means that only items matching the template id will be returned, otherwise they will be skipped.
If you need children of children mapped then you should add [SitecoreChildren] property on your SideMenuModel class as well (or refactor your code so the class references itself) or you could use a "get all descendants query (.//*[##templateid={SideMenuModel-GUID}]) although I would recommend that you ur he Content Search API at this point instead.
You can read more in the blog post about Getting child items with Glass.

Sitecore return dependant drop down list

I have the following set up in my CMS
Under ContactUsPageSettings I have some items that are using a template called ContactUsQuestionsOption and then below that I have some items that are using a template called ContactUsSubQuestionOption.
I want to return all of the ContactUsQuestionsOption in a drop down list, which I have managed to do. The problem I'm having is having a second drop down list that only returns the children of the selected item. Looking at the image above if Faculty Research and Publications was selected I'd want the second drop down to show BSR, Faculty Enquiries and ResearchSupportEnquiries.
I have a view model that looks like this:
[SitecoreType(AutoMap = true)]
public class ContactUsPageSettingsViewModel : ContactUsPageSettings
{
[SitecoreQuery(".//*[##templatekey='contactusquestionsoption']", IsLazy = false, IsRelative = true)]
public IEnumerable<ContactUsQuestionsOption> QuestionsOptionItems { get; set; }
[SitecoreQuery("./child::*", IsLazy = true, IsRelative = true)]
public virtual IEnumerable<ContactUsPageSettingsViewModel> Children { get; set; }
}
And a second view model like this:
[SitecoreType(AutoMap = true)]
public class QuestionsViewModel : ContactUsQuestionsOption
{
[SitecoreQuery(".//*[##templatekey='contactussubquestionsoption']", IsLazy = false, IsRelative = true)]
public virtual IEnumerable QuestionsOptionItems { get; set; }
}
But this doesn't appear to be the answer. Any ideas?
I finally worked out a solution for this. I changed my parent item in GlassBase adding the declaration [SitecoreParent(InferType = true)] above Parent. I can now filter my subquestions based on the parent id.
Thanks
Gemma :)
Since you have 3 unique data templates in Sitecore, you need 3 unique Glass-based models. Also, you don't have to use the query property in any of the classes, you can simply use the Children property.
Side note, though not necessary to accomplish what you are trying to do, you may find it helpful to ensure all of your Glass models have, at least, the properties in the GlassBase class below.
So heres what I would do:
Create a base class that contains these properties and update all other Glass models to inherit this class and they will have the children property properly mapped (as well as the Parent property).
public class GlassBase
{
public Guid Id { get; set; }
[SitecoreParent(InferType = true)]
public GlassBase Parent { get; set; }
[SitecoreChildren(InferType = true)]
public GlassBase Children { get; set; }
}
Now your template-specific models can simply inherit this class:
[SitecoreType(TemplateId = "Id of Data Template", AutoMap = true)]
public class ContactUsPageSettings : GlassBase
{
}
[SitecoreType(TemplateId = "Id of Data Template", AutoMap = true)]
public class ContactUsQuestionsOption : GlassBase
{
}
[SitecoreType(TemplateId = "Id of Data Template", AutoMap = true)]
public class ContactUsSubQuestionOption : GlassBase
{
}
I'm leaving out properties that map to each of the fields in these templates because I don't know what fields you have in each template.
Side note, make sure you set the TemplateId attribute of the SitecoreType decorator at the top of each of your Glass models to ensure they map to the proper data templates in Sitecore. GlassBase does not need the SitecoreType decorator.

Unit testing mvc model with HttpContext.Current.User.Identity.GetUserId()

In my MVC5 application, I have a Log entity, that is used to log any call to any controler. This log uses: HttpContext.Current.User.Identity.GetUserId() to determine the identity of the user accessing the controller.
public class Log
{
public Log()
{
TS = DateTime.Now;
UserId = HttpContext.Current.User.Identity.GetUserId();
}
[Required]
public Int32 Id { get; set; }
public string UserId { get; set; }
[Display(Name = "TS", ResourceType = typeof(Resources.Models.Log.Log))]
[Required(ErrorMessageResourceType = typeof(Resources.Models.Log.Log), ErrorMessageResourceName = "RequiredTS")]
public DateTime TS { get; set; }
[Required]
public short LogTypeId { get; set; }
[Display(Name = "LogText", ResourceType = typeof(Resources.Models.Log.Log))]
public string LogText { get; set; }
public ApplicationUser User { get; set; }
}
When I try to unit test a controller and crating an instance of the log class I get this error:
threw exception: System.NullReferenceException: Object reference not
set to an instance of an object.
at DASU.Core.Models.Log..ctor()
I know this is because the context is not set.
So my question is how do I set the context, or how do I mock the context, so I can create the Log for my test?
You should avoid coupling to HttpContext. Like suggested in the comments you could simplify your log my injecting the UserId into the dependent Log class
public class Log
{
public Log(string userId)
{
TS = DateTime.Now;
UserId = userId;
}
//...other code removed for brevity
}
or abstracting away the calls to HttpContext so that you can mock your abstract and inject that instead of trying and mock HttpContext
public interface IUserProvider {
string GetUserId();
}
You production implementations can wrap calls to HttpContext and you can easily create mock implementations for your unit tests.
public class Log
{
public Log(IUserProvider userProvider)
{
TS = DateTime.Now;
UserId = userProvider.GetUserId();
}
//...other code removed for brevity
}

Glass mapper does not load item childeren

We are facing issue in Glass mapper 4.0 where it does not load item children.
Here is our controller class , It is inheriting from GlassController:
public class CarouselController : GlassController
{
public ActionResult GetCarousel()
{
Model = this.GetDataSourceItem<CarouselViewModel>();
return View(Model);
}
}
And here is our View Model:
public class CarouselViewModel:Carousel_Folder
{
[SitecoreChildren]
public virtual IEnumerable<Carousel> Carousels { get; set; }
}
we get only the parent node information not the childeren (carousels) in the result
Here is the result we get:
[Result Image][1]
Also, following classes were generated with TDS:
[SitecoreType(TemplateId = ICarousel_FolderConstants.TemplateIdString )] //, Cachable = true
public partial interface ICarousel_Folder : IGlassBase
{}
Carousel template is inheriting from two templates content base and image base.
I had this issue before, for me i added [SitecoreChildren(IsLazy = false)] to my model and it works fine, in your case it should be like this :
public class CarouselViewModel:Carousel_Folder
{
[SitecoreChildren(IsLazy = false)]
public virtual IEnumerable<Carousel> Carousels { get; set; }
}
It seems that Carousels property is not par of the Carousel_Folder template, that's why your Interface/Class doesn't have something like:
[SitecoreType(TemplateId=ICarousel_FolderConstants.TemplateIdString)]
public partial class Carousel_Folder : GlassBase, ICarousel_Folder
{
[SitecoreField(ICarouselConstants.CarouselsFieldName)]
public virtual IEnumerable<Carousel> Carousels {get; set;}
}
In this case you will need to get the Parent item and get the children manually, i.e.:
var children = parentItem.Children.Select(x => x.GlassCast<Carousel>())

How to verify that method argument's property values are set when mocking methods with Moq?

Not sure if it has been asked before, here is the question.
Code first:
public class Customer {
public string Password { get; set; }
public string PasswordHash { get; set; }
}
public class CustomerService {
private ICustomerRepository _repo;
public CustomerService(ICustomerRepository repo) {
_repo = repo;
}
public int? AddCustomer(Customer customer) {
customer.PasswordHash = SHA1Hasher.ComputeHash(customer.Password);
return _repo.Add(customer);
}
}
public interface ICustomerRepository {
int? Add(Customer c);
}
public class CustomerRepository : ICustomerRepository {
int? AddCustomer(Customer customer) {
// call db and return identity
return 1;
}
}
[TestClass]
public class CustomerServiceTest {
[TestMethod]
public void Add_Should_Compute_Password_Hash_Before_Saving() {
var repoMock = new Mock<ICustomerRepository>();
//how do I make sure the password hash was calculated before passing the customer to repository???
}
}
How do I verify that CustomerService assigned the PasswordHash before passing the customer to repository?
There are several approaches you could take. Although not necessarily the best solution, here's one that doesn't require you to change your existing API. It assumes that SHA1Hasher.ComputeHash is a public method.
[TestClass]
public class CustomerServiceTest
{
[TestMethod]
public void Add_Should_Compute_Password_Hash_Before_Saving()
{
var customer = new Customer { Password = "Foo" };
var expectedHash = SHA1Hasher.ComputeHash(customer.Password);
var repoMock = new Mock<ICustomerRepository>();
repoMock
.Setup(r => r.Add(It.Is<Customer>(c => c.PasswordHash == expectedHash)))
.Returns(1)
.Verifiable();
// invoke service with customer and repoMock.Object here...
repoMock.Verify();
}
}
A slightly better solution would be to turn the SHA1Hasher into an injected service (such as IHasher) so that you can confirm that the PasswordHash property was assigned the value created by the IHasher instance.
Opening op your API even more, you could make the PasswordHash property virtual, so that you could pass a Mock Customer to the AddCustomer method to verify that the property was correctly set.
You could make SHA1Hasher non-static and virtual or wrap it in a ISHA1Hasher interface which can then be mocked. Wrapping static methods and objects in mockable classes is a classic way to increase testability.